From de56c3376e257fe92b8a34b438de579be1a32be8 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 26 May 2025 18:32:32 +0200 Subject: [PATCH 001/216] Bumped version to 20250526.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 169b86dbe8..9bc7f1e919 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250430.0" +version = "20250526.0" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From 1611423ca5fbdd14d07d866065af1fc9c5e3b3e1 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 26 May 2025 19:21:44 +0200 Subject: [PATCH 002/216] Fix duplicated items in strategy editor (#25600) --- src/components/ha-items-display-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ha-items-display-editor.ts b/src/components/ha-items-display-editor.ts index 77e1dfff35..e87ecfaef0 100644 --- a/src/components/ha-items-display-editor.ts +++ b/src/components/ha-items-display-editor.ts @@ -262,7 +262,7 @@ export class HaItemDisplayEditor extends LitElement { ]; } - return items.sort((a, b) => + return visibleItems.sort((a, b) => a.disableSorting && !b.disableSorting ? -1 : compare(a.value, b.value) ); } From 9131bf6dfd234b553a7f6b2c951431669ee6ba2c Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 27 May 2025 10:08:58 +0300 Subject: [PATCH 003/216] Fix double history graphs for a disabled entity (#25604) --- src/data/history.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/data/history.ts b/src/data/history.ts index fb784b54f4..553421d404 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -640,6 +640,12 @@ export const mergeHistoryResults = ( } for (const item of ltsResult.line) { + if (item.unit === BLANK_UNIT) { + // disabled entities have no unit, so we need to find the unit from the history result + item.unit = + historyResult.line.find((line) => line.identifier === item.identifier) + ?.unit ?? BLANK_UNIT; + } const key = computeGroupKey( item.unit, item.device_class, From 1a57eeddde9034b01fad9738eb1b432fa158e710 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Tue, 27 May 2025 10:22:04 +0200 Subject: [PATCH 004/216] Fix sidebar loading and demo (#25606) --- demo/src/stubs/frontend.ts | 23 +++++++++++++++++++++++ src/components/ha-sidebar.ts | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/demo/src/stubs/frontend.ts b/demo/src/stubs/frontend.ts index ae4ac073fd..70a4d5a0d2 100644 --- a/demo/src/stubs/frontend.ts +++ b/demo/src/stubs/frontend.ts @@ -1,7 +1,30 @@ import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; +let changeFunction; + export const mockFrontend = (hass: MockHomeAssistant) => { hass.mockWS("frontend/get_user_data", () => ({ value: null, })); + hass.mockWS("frontend/set_user_data", ({ key, value }) => { + if (key === "sidebar") { + changeFunction?.({ + value: { + panelOrder: value.panelOrder || [], + hiddenPanels: value.hiddenPanels || [], + }, + }); + } + }); + hass.mockWS("frontend/subscribe_user_data", (_msg, _hass, onChange) => { + changeFunction = onChange; + onChange?.({ + value: { + panelOrder: [], + hiddenPanels: [], + }, + }); + // eslint-disable-next-line @typescript-eslint/no-empty-function + return () => {}; + }); }; diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index d2f77a9770..ea303ab4c8 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -368,7 +368,7 @@ class HaSidebar extends SubscribeMixin(LitElement) { if (!this._panelOrder || !this._hiddenPanels) { return html` `; } From 77ee69b64d1cf61ff653fce96adfa8c1102fcece Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Tue, 27 May 2025 12:55:20 +0200 Subject: [PATCH 005/216] Fix font settings for button card (#25607) --- src/panels/lovelace/cards/hui-button-card.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index f6cc9b0bcd..957c8a868b 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -224,19 +224,19 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { filter: colored ? stateColorBrightness(stateObj) : undefined, height: this._config.icon_height ? this._config.icon_height - : "", + : undefined, })} > ` - : ""} + : nothing} ${this._config.show_name ? html`${name}` - : ""} + : nothing} ${this._config.show_state && stateObj ? html` ${this.hass.formatEntityState(stateObj)} ` - : ""} + : nothing} `; } @@ -282,7 +282,8 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { align-items: center; text-align: center; padding: 4% 0; - font-size: 16.8px; + font-size: var(--ha-font-size-l); + line-height: var(--ha-line-height-condensed); height: 100%; box-sizing: border-box; justify-content: center; From 116716c51da210677e7313e4a82589d94be40c7f Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 27 May 2025 21:43:32 +0300 Subject: [PATCH 006/216] Fix duplicate legend items when comparing energy data (#25610) --- src/components/chart/ha-chart-base.ts | 40 ++++++++++++------- .../hui-energy-devices-detail-graph-card.ts | 18 +++++++++ 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 64db12b3df..2ecad752a3 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -220,11 +220,11 @@ export class HaChartBase extends LitElement { return nothing; } const datasets = ensureArray(this.data); - const items = (legend.data || - datasets + const items: LegendComponentOption["data"] = + legend.data || + ((datasets .filter((d) => (d.data as any[])?.length && (d.id || d.name)) - .map((d) => d.name ?? d.id) || - []) as string[]; + .map((d) => d.name ?? d.id) || []) as string[]); const isMobile = window.matchMedia( "all and (max-width: 450px), all and (max-height: 500px)" @@ -239,20 +239,32 @@ export class HaChartBase extends LitElement { })} >
    - ${items.map((item: string, index: number) => { + ${items.map((item, index) => { if (!this.expandLegend && index >= overflowLimit) { return nothing; } - const dataset = datasets.find( - (d) => d.id === item || d.name === item - ); - const color = dataset?.color as string; - const borderColor = dataset?.itemStyle?.borderColor as string; + let itemStyle: Record = {}; + let name = ""; + if (typeof item === "string") { + name = item; + const dataset = datasets.find( + (d) => d.id === item || d.name === item + ); + itemStyle = { + color: dataset?.color as string, + ...(dataset?.itemStyle as { borderColor?: string }), + }; + } else { + name = item.name ?? ""; + itemStyle = item.itemStyle ?? {}; + } + const color = itemStyle?.color as string; + const borderColor = itemStyle?.borderColor as string; return html`
  • -
    ${item}
    +
    ${name}
  • `; })} ${items.length > overflowLimit diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts index c3d9c5cc23..917d9e5950 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts @@ -6,6 +6,7 @@ import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; import type { BarSeriesOption } from "echarts/charts"; +import type { LegendComponentOption } from "echarts/components"; import { getGraphColorByIndex } from "../../../../common/color/colors"; import { getEnergyColor } from "./common/color"; import "../../../../components/ha-card"; @@ -54,6 +55,8 @@ export class HuiEnergyDevicesDetailGraphCard @state() private _data?: EnergyData; + @state() private _legendData?: LegendComponentOption["data"]; + @state() private _start = startOfToday(); @state() private _end = endOfToday(); @@ -185,6 +188,7 @@ export class HuiEnergyDevicesDetailGraphCard legend: { show: true, type: "custom", + data: this._legendData, selected: this._hiddenStats.reduce((acc, stat) => { acc[stat] = false; return acc; @@ -310,6 +314,13 @@ export class HuiEnergyDevicesDetailGraphCard ); datasets.push(...processedData); + this._legendData = processedData.map((d) => ({ + name: d.name as string, + itemStyle: { + color: d.color as string, + borderColor: d.itemStyle?.borderColor as string, + }, + })); if (showUntracked) { const untrackedData = this._processUntracked( @@ -319,6 +330,13 @@ export class HuiEnergyDevicesDetailGraphCard false ); datasets.push(untrackedData); + this._legendData.push({ + name: untrackedData.name as string, + itemStyle: { + color: untrackedData.color as string, + borderColor: untrackedData.itemStyle?.borderColor as string, + }, + }); } fillDataGapsAndRoundCaps(datasets); From 6abdeeae209afe51b26d232f934c4098a8143a01 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 27 May 2025 21:46:03 +0300 Subject: [PATCH 007/216] Fix for history graph with tiny values (#25612) --- .../chart/state-history-chart-line.ts | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index 5e11621b60..53cc3b926b 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -82,6 +82,8 @@ export class StateHistoryChartLine extends LitElement { private _chartTime: Date = new Date(); + private _previousYAxisLabelValue = 0; + protected render() { return html` { - const formatOptions = - value >= 1 || value <= -1 - ? undefined - : { - // show the first significant digit for tiny values - maximumFractionDigits: Math.max( - 2, - -Math.floor(Math.log10(Math.abs(value % 1 || 1))) - ), - }; - const label = formatNumber( - value, - this.hass.locale, - formatOptions - ); - const width = measureTextWidth(label, 12) + 5; - if (width > this._yWidth) { - this._yWidth = width; - fireEvent(this, "y-width-changed", { - value: this._yWidth, - chartIndex: this.chartIndex, - }); - } - return label; - }, + formatter: this._formatYAxisLabel, }, } as YAXisOption, legend: { @@ -745,6 +722,33 @@ export class StateHistoryChartLine extends LitElement { this._visualMap = visualMap.length > 0 ? visualMap : undefined; } + private _formatYAxisLabel = (value: number) => { + const formatOptions = + value >= 1 || value <= -1 + ? undefined + : { + // show the first significant digit for tiny values + maximumFractionDigits: Math.max( + 2, + // use the difference to the previous value to determine the number of significant digits #25526 + -Math.floor( + Math.log10(Math.abs(value - this._previousYAxisLabelValue || 1)) + ) + ), + }; + const label = formatNumber(value, this.hass.locale, formatOptions); + const width = measureTextWidth(label, 12) + 5; + if (width > this._yWidth) { + this._yWidth = width; + fireEvent(this, "y-width-changed", { + value: this._yWidth, + chartIndex: this.chartIndex, + }); + } + this._previousYAxisLabelValue = value; + return label; + }; + private _clampYAxis(value?: number | ((values: any) => number)) { if (this.logarithmicScale) { // log(0) is -Infinity, so we need to set a minimum value From ae49de8e71cf9454fb9c17d9019725a7a1344840 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Tue, 27 May 2025 19:56:55 +0200 Subject: [PATCH 008/216] Fix typo in restore_entity_id_selected::confirm_text (#25615) --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 02dc75f9d2..c24fbd49b8 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5131,7 +5131,7 @@ "restore_entity_id_selected": { "button": "Recreate entity IDs of selected", "confirm_title": "Recreate entity IDs?", - "confirm_text": "Are you sure you want to change the entity IDs of these entities? You will have to change you dashboards, automations and scripts to use the new entity IDs.", + "confirm_text": "Are you sure you want to change the entity IDs of these entities? You will have to change your dashboards, automations and scripts to use the new entity IDs.", "changes": "The following entity IDs will be updated:" }, "delete_selected": { From 06270c771f0f6562d1720a1adc97308fe71664e3 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 27 May 2025 21:43:42 +0200 Subject: [PATCH 009/216] Bumped version to 20250527.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9bc7f1e919..839caacc93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250526.0" +version = "20250527.0" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From 61d9b0d2a3b27d57afb6a4d93e255e05aa5e82f5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 May 2025 15:03:17 +0200 Subject: [PATCH 010/216] Improve action picker UI and search (#25525) --- src/common/entity/valid_service_id.ts | 4 + src/components/entity/ha-entity-picker.ts | 9 +- src/components/ha-service-control.ts | 8 +- src/components/ha-service-picker.ts | 239 +++++++++++------- .../action/developer-tools-action.ts | 2 + .../state/developer-tools-state.ts | 1 + src/translations/en.json | 3 +- 7 files changed, 177 insertions(+), 89 deletions(-) create mode 100644 src/common/entity/valid_service_id.ts diff --git a/src/common/entity/valid_service_id.ts b/src/common/entity/valid_service_id.ts new file mode 100644 index 0000000000..97c88f6907 --- /dev/null +++ b/src/common/entity/valid_service_id.ts @@ -0,0 +1,4 @@ +const validServiceId = /^(\w+)\.(\w+)$/; + +export const isValidServiceId = (actionId: string) => + validServiceId.test(actionId); diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index ef39a4ef14..8d29711e52 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -51,6 +51,9 @@ export class HaEntityPicker extends LitElement { @property({ type: Boolean, attribute: "allow-custom-entity" }) public allowCustomEntity; + @property({ type: Boolean, attribute: "show-entity-id" }) + public showEntityId = false; + @property() public label?: string; @property() public value?: string; @@ -166,11 +169,15 @@ export class HaEntityPicker extends LitElement { `; }; + private get _showEntityId() { + return this.showEntityId || this.hass.userData?.showEntityIdPicker; + } + private _rowRenderer: ComboBoxLitRenderer = ( item, { index } ) => { - const showEntityId = this.hass.userData?.showEntityIdPicker; + const showEntityId = this._showEntityId; return html` diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts index 82f7ae547e..a31382ac2f 100644 --- a/src/components/ha-service-control.ts +++ b/src/components/ha-service-control.ts @@ -85,8 +85,11 @@ export class HaServiceControl extends LitElement { @property({ type: Boolean }) public narrow = false; - @property({ attribute: "show-advanced", type: Boolean }) public showAdvanced = - false; + @property({ attribute: "show-advanced", type: Boolean }) + public showAdvanced = false; + + @property({ attribute: "show-service-id", type: Boolean }) + public showServiceId = false; @property({ attribute: "hide-picker", type: Boolean, reflect: true }) public hidePicker = false; @@ -435,6 +438,7 @@ export class HaServiceControl extends LitElement { .value=${this._value?.action} .disabled=${this.disabled} @value-changed=${this._serviceChanged} + .showServiceId=${this.showServiceId} >`} ${this.hideDescription ? nothing diff --git a/src/components/ha-service-picker.ts b/src/components/ha-service-picker.ts index a72a1a463c..2d9c6c0853 100644 --- a/src/components/ha-service-picker.ts +++ b/src/components/ha-service-picker.ts @@ -1,15 +1,25 @@ +import { mdiRoomService } from "@mdi/js"; import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit"; -import { html, LitElement } from "lit"; -import { customElement, property, state } from "lit/decorators"; +import { html, LitElement, nothing, type TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; +import { isValidServiceId } from "../common/entity/valid_service_id"; import type { LocalizeFunc } from "../common/translations/localize"; -import { domainToName } from "../data/integration"; -import type { HomeAssistant } from "../types"; -import "./ha-combo-box"; -import "./ha-combo-box-item"; -import "./ha-service-icon"; import { getServiceIcons } from "../data/icons"; +import { domainToName } from "../data/integration"; +import type { HomeAssistant, ValueChangedEvent } from "../types"; +import "./ha-combo-box-item"; +import "./ha-generic-picker"; +import type { HaGenericPicker } from "./ha-generic-picker"; +import type { PickerComboBoxItem } from "./ha-picker-combo-box"; +import type { PickerValueRenderer } from "./ha-picker-field"; +import "./ha-service-icon"; + +interface ServiceComboBoxItem extends PickerComboBoxItem { + domain_name?: string; + service_id?: string; +} @customElement("ha-service-picker") class HaServicePicker extends LitElement { @@ -17,66 +27,121 @@ class HaServicePicker extends LitElement { @property({ type: Boolean }) public disabled = false; + @property() public label?: string; + + @property() public placeholder?: string; + @property() public value?: string; - @state() private _filter?: string; + @property({ attribute: "show-service-id", type: Boolean }) + public showServiceId = false; - protected willUpdate() { - if (!this.hasUpdated) { - this.hass.loadBackendTranslation("services"); - getServiceIcons(this.hass); - } + @query("ha-generic-picker") private _picker?: HaGenericPicker; + + public async open() { + await this.updateComplete; + await this._picker?.open(); } - private _rowRenderer: ComboBoxLitRenderer<{ service: string; name: string }> = - (item) => html` - - - ${item.name} - ${item.name === item.service ? "" : item.service} - - `; + protected firstUpdated(props) { + super.firstUpdated(props); + this.hass.loadBackendTranslation("services"); + getServiceIcons(this.hass); + } - protected render() { - return html` - = ( + item, + { index } + ) => html` + + + ${item.primary} + ${item.secondary} + ${item.service_id && this.showServiceId + ? html` + ${item.service_id} + ` + : nothing} + ${item.domain_name + ? html` +
    + ${item.domain_name} +
    + ` + : nothing} +
    + `; + + private _valueRenderer: PickerValueRenderer = (value) => { + const serviceId = value; + const [domain, service] = serviceId.split("."); + + if (!this.hass.services[domain]?.[service]) { + return html` + + ${value} + `; + } + + const serviceName = + this.hass.localize(`component.${domain}.services.${service}.name`) || + this.hass.services[domain][service].name || + service; + + return html` + + ${serviceName} + ${this.showServiceId + ? html`${serviceId}` + : nothing} + `; + }; + + protected render(): TemplateResult { + const placeholder = + this.placeholder ?? + this.hass.localize("ui.components.service-picker.action"); + + return html` +
    + > + `; } + private _getItems = () => + this._services(this.hass.localize, this.hass.services); + private _services = memoizeOne( ( localize: LocalizeFunc, services: HomeAssistant["services"] - ): { - service: string; - name: string; - }[] => { + ): ServiceComboBoxItem[] => { if (!services) { return []; } - const result: { service: string; name: string }[] = []; + const items: ServiceComboBoxItem[] = []; Object.keys(services) .sort() @@ -84,56 +149,60 @@ class HaServicePicker extends LitElement { const services_keys = Object.keys(services[domain]).sort(); for (const service of services_keys) { - result.push({ - service: `${domain}.${service}`, - name: `${domainToName(localize, domain)}: ${ - this.hass.localize( - `component.${domain}.services.${service}.name` - ) || - services[domain][service].name || - service - }`, + const serviceId = `${domain}.${service}`; + const domainName = domainToName(localize, domain); + + const name = + this.hass.localize( + `component.${domain}.services.${service}.name` + ) || + services[domain][service].name || + service; + + const description = + this.hass.localize( + `component.${domain}.services.${service}.description` + ) || services[domain][service].description; + + items.push({ + id: serviceId, + primary: name, + secondary: description, + domain_name: domainName, + service_id: serviceId, + search_labels: [serviceId, domainName, name, description].filter( + Boolean + ), + sorting_label: serviceId, }); } }); - return result; + return items; } ); - private _filteredServices = memoizeOne( - ( - localize: LocalizeFunc, - services: HomeAssistant["services"], - filter?: string - ) => { - if (!services) { - return []; - } - const processedServices = this._services(localize, services); + private _valueChanged(ev: ValueChangedEvent) { + ev.stopPropagation(); + const value = ev.detail.value; - if (!filter) { - return processedServices; - } - const split_filter = filter.split(" "); - return processedServices.filter((service) => { - const lower_service_name = service.name.toLowerCase(); - const lower_service = service.service.toLowerCase(); - return split_filter.every( - (f) => lower_service_name.includes(f) || lower_service.includes(f) - ); - }); + if (!value) { + this._setValue(undefined); + return; } - ); - private _filterChanged(ev: CustomEvent): void { - this._filter = ev.detail.value.toLowerCase(); + if (!isValidServiceId(value)) { + return; + } + + this._setValue(value); } - private _valueChanged(ev) { - this.value = ev.detail.value; + private _setValue(value: string | undefined) { + this.value = value; + + fireEvent(this, "value-changed", { value }); fireEvent(this, "change"); - fireEvent(this, "value-changed", { value: this.value }); } } diff --git a/src/panels/developer-tools/action/developer-tools-action.ts b/src/panels/developer-tools/action/developer-tools-action.ts index 610b53be71..d964555e27 100644 --- a/src/panels/developer-tools/action/developer-tools-action.ts +++ b/src/panels/developer-tools/action/developer-tools-action.ts @@ -142,6 +142,7 @@ class HaPanelDevAction extends LitElement { .hass=${this.hass} .value=${this._serviceData?.action} @value-changed=${this._serviceChanged} + show-service-id > diff --git a/src/panels/developer-tools/state/developer-tools-state.ts b/src/panels/developer-tools/state/developer-tools-state.ts index 5b9596b3a0..30c76c952e 100644 --- a/src/panels/developer-tools/state/developer-tools-state.ts +++ b/src/panels/developer-tools/state/developer-tools-state.ts @@ -130,6 +130,7 @@ class HaPanelDevState extends LitElement { .value=${this._entityId} @value-changed=${this._entityIdChanged} allow-custom-entity + show-entity-id > ${this._entityId ? html` diff --git a/src/translations/en.json b/src/translations/en.json index c24fbd49b8..7fab97a767 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -873,7 +873,8 @@ } }, "service-picker": { - "action": "Action" + "action": "Action", + "no_match": "No matching actions found" }, "service-control": { "required": "This field is required", From 5371fd649c418ba033f5b32420fa7ce5ab8b025d Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 28 May 2025 10:04:54 +0200 Subject: [PATCH 011/216] Set markdown code line-height (#25618) --- src/components/ha-markdown.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ha-markdown.ts b/src/components/ha-markdown.ts index 51d4fdd49e..9e684e01ca 100644 --- a/src/components/ha-markdown.ts +++ b/src/components/ha-markdown.ts @@ -77,7 +77,7 @@ export class HaMarkdown extends LitElement { pre { padding: 16px; overflow: auto; - line-height: 1.45; + line-height: var(--ha-line-height-condensed); font-family: var(--ha-font-family-code); } h1, From b907dbefad278aae5cab03791a3fe69c5b39ff06 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 May 2025 12:04:54 +0200 Subject: [PATCH 012/216] Force narrow style for action, condition and trigger in flows (#25619) --- src/components/ha-form/ha-form.ts | 3 +++ src/components/ha-selector/ha-selector-action.ts | 3 +++ src/components/ha-selector/ha-selector-condition.ts | 3 +++ src/components/ha-selector/ha-selector-trigger.ts | 3 +++ src/components/ha-selector/ha-selector.ts | 3 +++ src/dialogs/config-flow/dialog-data-entry-flow.ts | 1 + src/dialogs/config-flow/step-flow-form.ts | 3 +++ 7 files changed, 19 insertions(+) diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts index cbca7aca3c..fbc79a56dd 100644 --- a/src/components/ha-form/ha-form.ts +++ b/src/components/ha-form/ha-form.ts @@ -34,6 +34,8 @@ const getWarning = (obj, item) => (obj && item.name ? obj[item.name] : null); export class HaForm extends LitElement implements HaFormElement { @property({ attribute: false }) public hass?: HomeAssistant; + @property({ type: Boolean }) public narrow = false; + @property({ attribute: false }) public data!: HaFormDataContainer; @property({ attribute: false }) public schema!: readonly HaFormSchema[]; @@ -135,6 +137,7 @@ export class HaForm extends LitElement implements HaFormElement { ? html` `; } diff --git a/src/components/ha-selector/ha-selector-condition.ts b/src/components/ha-selector/ha-selector-condition.ts index 512b9657be..b96bd2894a 100644 --- a/src/components/ha-selector/ha-selector-condition.ts +++ b/src/components/ha-selector/ha-selector-condition.ts @@ -9,6 +9,8 @@ import type { HomeAssistant } from "../../types"; export class HaConditionSelector extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ type: Boolean }) public narrow = false; + @property({ attribute: false }) public selector!: ConditionSelector; @property({ attribute: false }) public value?: Condition; @@ -24,6 +26,7 @@ export class HaConditionSelector extends LitElement { .disabled=${this.disabled} .conditions=${this.value || []} .hass=${this.hass} + .narrow=${this.narrow} > `; } diff --git a/src/components/ha-selector/ha-selector-trigger.ts b/src/components/ha-selector/ha-selector-trigger.ts index 9a580d8d5a..110a744b6e 100644 --- a/src/components/ha-selector/ha-selector-trigger.ts +++ b/src/components/ha-selector/ha-selector-trigger.ts @@ -11,6 +11,8 @@ import type { HomeAssistant } from "../../types"; export class HaTriggerSelector extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ type: Boolean }) public narrow = false; + @property({ attribute: false }) public selector!: TriggerSelector; @property({ attribute: false }) public value?: Trigger; @@ -33,6 +35,7 @@ export class HaTriggerSelector extends LitElement { .disabled=${this.disabled} .triggers=${this._triggers(this.value)} .hass=${this.hass} + .narrow=${this.narrow} > `; } diff --git a/src/components/ha-selector/ha-selector.ts b/src/components/ha-selector/ha-selector.ts index 69a5b59a25..47c2bdd8dd 100644 --- a/src/components/ha-selector/ha-selector.ts +++ b/src/components/ha-selector/ha-selector.ts @@ -69,6 +69,8 @@ const LEGACY_UI_SELECTORS = new Set(["ui-action", "ui-color"]); export class HaSelector extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ type: Boolean }) public narrow = false; + @property() public name?: string; @property({ attribute: false }) public selector!: Selector; @@ -127,6 +129,7 @@ export class HaSelector extends LitElement { return html` ${dynamicElement(`ha-selector-${this._type}`, { hass: this.hass, + narrow: this.narrow, name: this.name, selector: this._handleLegacySelector(this.selector), value: this.value, diff --git a/src/dialogs/config-flow/dialog-data-entry-flow.ts b/src/dialogs/config-flow/dialog-data-entry-flow.ts index 5f141c3572..298f5742e2 100644 --- a/src/dialogs/config-flow/dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/dialog-data-entry-flow.ts @@ -349,6 +349,7 @@ class DataEntryFlowDialog extends LitElement { ${this._step.type === "form" ? html` Date: Wed, 28 May 2025 13:06:45 +0200 Subject: [PATCH 013/216] Fix missing helper for entity picker (#25622) --- src/components/entity/ha-entity-picker.ts | 1 + src/components/ha-generic-picker.ts | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 8d29711e52..7803f079f7 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -397,6 +397,7 @@ export class HaEntityPicker extends LitElement { .autofocus=${this.autofocus} .allowCustomValue=${this.allowCustomEntity} .label=${this.label} + .helper=${this.helper} .searchLabel=${this.searchLabel} .notFoundLabel=${notFoundLabel} .placeholder=${placeholder} diff --git a/src/components/ha-generic-picker.ts b/src/components/ha-generic-picker.ts index 2c699382fd..687a548ede 100644 --- a/src/components/ha-generic-picker.ts +++ b/src/components/ha-generic-picker.ts @@ -104,8 +104,8 @@ export class HaGenericPicker extends LitElement { .getAdditionalItems=${this.getAdditionalItems} > `} - ${this._renderHelper()} + ${this._renderHelper()} `; } @@ -164,6 +164,10 @@ export class HaGenericPicker extends LitElement { display: block; margin: 0 0 8px; } + ha-input-helper-text { + display: block; + margin: 8px 0 0; + } `, ]; } From 139c8b3702eaac89967fb1e15c8750a7a0d93c6e Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 May 2025 13:07:22 +0200 Subject: [PATCH 014/216] Fix picker field height (#25623) --- src/components/ha-picker-field.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ha-picker-field.ts b/src/components/ha-picker-field.ts index 62663d2dfd..0d62e084bc 100644 --- a/src/components/ha-picker-field.ts +++ b/src/components/ha-picker-field.ts @@ -95,8 +95,8 @@ export class HaPickerField extends LitElement { border-end-start-radius: 0; --md-list-item-one-line-container-height: 56px; --md-list-item-two-line-container-height: 56px; - --md-list-item-top-space: 8px; - --md-list-item-bottom-space: 8px; + --md-list-item-top-space: 0px; + --md-list-item-bottom-space: 0px; --md-list-item-leading-space: 8px; --md-list-item-trailing-space: 8px; --ha-md-list-item-gap: 8px; From 48a3e1fd63811a6809a0d1ec90f4f32b25900cfc Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 May 2025 22:33:42 +0200 Subject: [PATCH 015/216] Put item at the top of picker result if there is an exact match with entity id (#25625) --- src/components/entity/ha-entity-picker.ts | 23 ++++++++++++++++++- src/components/entity/ha-statistic-picker.ts | 24 +++++++++++++++++++- src/components/ha-generic-picker.ts | 5 ++++ src/components/ha-picker-combo-box.ts | 20 +++++++++++++--- 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/components/entity/ha-entity-picker.ts b/src/components/entity/ha-entity-picker.ts index 7803f079f7..ae7aa71088 100644 --- a/src/components/entity/ha-entity-picker.ts +++ b/src/components/entity/ha-entity-picker.ts @@ -23,7 +23,10 @@ import type { HomeAssistant } from "../../types"; import "../ha-combo-box-item"; import "../ha-generic-picker"; import type { HaGenericPicker } from "../ha-generic-picker"; -import type { PickerComboBoxItem } from "../ha-picker-combo-box"; +import type { + PickerComboBoxItem, + PickerComboBoxSearchFn, +} from "../ha-picker-combo-box"; import type { PickerValueRenderer } from "../ha-picker-field"; import "../ha-svg-icon"; import "./state-badge"; @@ -406,6 +409,7 @@ export class HaEntityPicker extends LitElement { .getItems=${this._getItems} .getAdditionalItems=${this._getAdditionalItems} .hideClearIcon=${this.hideClearIcon} + .searchFn=${this._searchFn} .valueRenderer=${this._valueRenderer} @value-changed=${this._valueChanged} > @@ -413,6 +417,23 @@ export class HaEntityPicker extends LitElement { `; } + private _searchFn: PickerComboBoxSearchFn = ( + search, + filteredItems + ) => { + // If there is exact match for entity id, put it first + const index = filteredItems.findIndex( + (item) => item.stateObj?.entity_id === search + ); + if (index === -1) { + return filteredItems; + } + + const [exactMatch] = filteredItems.splice(index, 1); + filteredItems.unshift(exactMatch); + return filteredItems; + }; + public async open() { await this.updateComplete; await this._picker?.open(); diff --git a/src/components/entity/ha-statistic-picker.ts b/src/components/entity/ha-statistic-picker.ts index 955071a8f2..3d20ab7a43 100644 --- a/src/components/entity/ha-statistic-picker.ts +++ b/src/components/entity/ha-statistic-picker.ts @@ -25,7 +25,10 @@ import "../ha-generic-picker"; import type { HaGenericPicker } from "../ha-generic-picker"; import "../ha-icon-button"; import "../ha-input-helper-text"; -import type { PickerComboBoxItem } from "../ha-picker-combo-box"; +import type { + PickerComboBoxItem, + PickerComboBoxSearchFn, +} from "../ha-picker-combo-box"; import type { PickerValueRenderer } from "../ha-picker-field"; import "../ha-svg-icon"; import "./state-badge"; @@ -470,6 +473,7 @@ export class HaStatisticPicker extends LitElement { .getItems=${this._getItems} .getAdditionalItems=${this._getAdditionalItems} .hideClearIcon=${this.hideClearIcon} + .searchFn=${this._searchFn} .valueRenderer=${this._valueRenderer} @value-changed=${this._valueChanged} > @@ -477,6 +481,24 @@ export class HaStatisticPicker extends LitElement { `; } + private _searchFn: PickerComboBoxSearchFn = ( + search, + filteredItems + ) => { + // If there is exact match for entity id or statistic id, put it first + const index = filteredItems.findIndex( + (item) => + item.stateObj?.entity_id === search || item.statistic_id === search + ); + if (index === -1) { + return filteredItems; + } + + const [exactMatch] = filteredItems.splice(index, 1); + filteredItems.unshift(exactMatch); + return filteredItems; + }; + private _valueChanged(ev: ValueChangedEvent) { ev.stopPropagation(); const value = ev.detail.value; diff --git a/src/components/ha-generic-picker.ts b/src/components/ha-generic-picker.ts index 687a548ede..92631333cf 100644 --- a/src/components/ha-generic-picker.ts +++ b/src/components/ha-generic-picker.ts @@ -12,6 +12,7 @@ import "./ha-picker-combo-box"; import type { HaPickerComboBox, PickerComboBoxItem, + PickerComboBoxSearchFn, } from "./ha-picker-combo-box"; import "./ha-picker-field"; import type { HaPickerField, PickerValueRenderer } from "./ha-picker-field"; @@ -57,6 +58,9 @@ export class HaGenericPicker extends LitElement { @property({ attribute: false }) public valueRenderer?: PickerValueRenderer; + @property({ attribute: false }) + public searchFn?: PickerComboBoxSearchFn; + @property({ attribute: "not-found-label", type: String }) public notFoundLabel?: string; @@ -102,6 +106,7 @@ export class HaGenericPicker extends LitElement { .notFoundLabel=${this.notFoundLabel} .getItems=${this.getItems} .getAdditionalItems=${this.getAdditionalItems} + .searchFn=${this.searchFn} > `} diff --git a/src/components/ha-picker-combo-box.ts b/src/components/ha-picker-combo-box.ts index 5280909ef8..9ea33ee20c 100644 --- a/src/components/ha-picker-combo-box.ts +++ b/src/components/ha-picker-combo-box.ts @@ -49,6 +49,12 @@ const DEFAULT_ROW_RENDERER: ComboBoxLitRenderer = (
    `; +export type PickerComboBoxSearchFn = ( + search: string, + filteredItems: T[], + allItems: T[] +) => T[]; + @customElement("ha-picker-combo-box") export class HaPickerComboBox extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -84,6 +90,9 @@ export class HaPickerComboBox extends LitElement { @property({ attribute: "not-found-label", type: String }) public notFoundLabel?: string; + @property({ attribute: false }) + public searchFn?: PickerComboBoxSearchFn; + @state() private _opened = false; @query("ha-combo-box", true) public comboBox!: HaComboBox; @@ -237,6 +246,7 @@ export class HaPickerComboBox extends LitElement { const fuse = new HaFuse(this._items, { shouldSort: false }, index); const results = fuse.multiTermsSearch(searchString); + let filteredItems = this._items as PickerComboBoxItem[]; if (results) { const items = results.map((result) => result.item); if (items.length === 0) { @@ -246,10 +256,14 @@ export class HaPickerComboBox extends LitElement { } const additionalItems = this._getAdditionalItems(searchString); items.push(...additionalItems); - target.filteredItems = items; - } else { - target.filteredItems = this._items; + filteredItems = items; } + + if (this.searchFn) { + filteredItems = this.searchFn(searchString, filteredItems, this._items); + } + + target.filteredItems = filteredItems; } private _setValue(value: string | undefined) { From a1819d6189e10dab5f7aeccd0e7c0e0789214366 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 May 2025 19:38:33 +0200 Subject: [PATCH 016/216] Improve search in add automation element dialog (#25626) * Improve search in add automation element dialog * Remove unexpected import * Take min character search into account --- .../add-automation-element-dialog.ts | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/panels/config/automation/add-automation-element-dialog.ts b/src/panels/config/automation/add-automation-element-dialog.ts index 86dd1ee02e..729df0bba3 100644 --- a/src/panels/config/automation/add-automation-element-dialog.ts +++ b/src/panels/config/automation/add-automation-element-dialog.ts @@ -1,5 +1,4 @@ import { mdiClose, mdiContentPaste, mdiPlus } from "@mdi/js"; -import type { IFuseOptions } from "fuse.js"; import Fuse from "fuse.js"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; @@ -46,6 +45,7 @@ import { haStyle, haStyleDialog } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import type { AddAutomationElementDialogParams } from "./show-add-automation-element-dialog"; import { PASTE_VALUE } from "./show-add-automation-element-dialog"; +import { HaFuse } from "../../../resources/fuse"; const TYPES = { trigger: { groups: TRIGGER_GROUPS, icons: TRIGGER_ICONS }, @@ -175,6 +175,40 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { type: AddAutomationElementDialogParams["type"], group: string | undefined, filter: string, + domains: Set | undefined, + localize: LocalizeFunc, + services: HomeAssistant["services"], + manifests?: DomainManifestLookup + ): ListItem[] => { + const items = this._items(type, group, localize, services, manifests); + + const index = this._fuseIndex(items); + + const fuse = new HaFuse( + items, + { ignoreLocation: true, includeScore: true }, + index + ); + + const results = fuse.multiTermsSearch(filter); + if (results) { + return results.map((result) => result.item); + } + return this._getGroupItems( + type, + group, + domains, + localize, + services, + manifests + ); + } + ); + + private _items = memoizeOne( + ( + type: AddAutomationElementDialogParams["type"], + group: string | undefined, localize: LocalizeFunc, services: HomeAssistant["services"], manifests?: DomainManifestLookup @@ -189,24 +223,17 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { ); const items = flattenGroups(groups).flat(); - if (type === "action") { items.push(...this._services(localize, services, manifests, group)); } - - const options: IFuseOptions = { - keys: ["key", "name", "description"], - isCaseSensitive: false, - ignoreLocation: true, - minMatchCharLength: Math.min(filter.length, 2), - threshold: 0.2, - ignoreDiacritics: true, - }; - const fuse = new Fuse(items, options); - return fuse.search(filter).map((result) => result.item); + return items; } ); + private _fuseIndex = memoizeOne((items: ListItem[]) => + Fuse.createIndex(["key", "name", "description"], items) + ); + private _getGroupItems = memoizeOne( ( type: AddAutomationElementDialogParams["type"], @@ -449,6 +476,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { this._params.type, this._group, this._filter, + this._domains, this.hass.localize, this.hass.services, this._manifests From 46e05f10d116872fcf4171d59ada8b872e945c9c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 28 May 2025 22:35:50 +0200 Subject: [PATCH 017/216] Bumped version to 20250528.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 839caacc93..0fc881abf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250527.0" +version = "20250528.0" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From 9f69347e1d0889d4feffb92ecf284e0f8714836f Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 28 May 2025 22:53:30 -0700 Subject: [PATCH 018/216] Cleanup some styling on disabled entity picker (#25632) --- src/components/ha-generic-picker.ts | 11 +++++++++-- src/components/ha-input-helper-text.ts | 7 ++++++- src/components/ha-multi-textfield.ts | 4 +++- src/components/ha-picker-field.ts | 12 ++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/components/ha-generic-picker.ts b/src/components/ha-generic-picker.ts index 92631333cf..98496a4b40 100644 --- a/src/components/ha-generic-picker.ts +++ b/src/components/ha-generic-picker.ts @@ -72,7 +72,9 @@ export class HaGenericPicker extends LitElement { protected render() { return html` - ${this.label ? html`` : nothing} + ${this.label + ? html`` + : nothing}
    ${!this._opened ? html` @@ -116,7 +118,9 @@ export class HaGenericPicker extends LitElement { private _renderHelper() { return this.helper - ? html`${this.helper}` + ? html`${this.helper}` : nothing; } @@ -165,6 +169,9 @@ export class HaGenericPicker extends LitElement { position: relative; display: block; } + label[disabled] { + color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.6)); + } label { display: block; margin: 0 0 8px; diff --git a/src/components/ha-input-helper-text.ts b/src/components/ha-input-helper-text.ts index 4af842ee74..6d817d1bf6 100644 --- a/src/components/ha-input-helper-text.ts +++ b/src/components/ha-input-helper-text.ts @@ -1,9 +1,11 @@ import type { TemplateResult } from "lit"; import { css, html, LitElement } from "lit"; -import { customElement } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; @customElement("ha-input-helper-text") class InputHelperText extends LitElement { + @property({ type: Boolean, reflect: true }) disabled = false; + protected render(): TemplateResult { return html``; } @@ -18,6 +20,9 @@ class InputHelperText extends LitElement { padding-inline-start: 16px; padding-inline-end: 16px; } + :host([disabled]) { + color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.6)); + } `; } diff --git a/src/components/ha-multi-textfield.ts b/src/components/ha-multi-textfield.ts index dd1d46b8b2..c40b6033b6 100644 --- a/src/components/ha-multi-textfield.ts +++ b/src/components/ha-multi-textfield.ts @@ -85,7 +85,9 @@ class HaMultiTextField extends LitElement {
    ${this.helper - ? html`${this.helper}` + ? html`${this.helper}` : nothing} `; } diff --git a/src/components/ha-picker-field.ts b/src/components/ha-picker-field.ts index 0d62e084bc..ba8513794f 100644 --- a/src/components/ha-picker-field.ts +++ b/src/components/ha-picker-field.ts @@ -88,6 +88,12 @@ export class HaPickerField extends LitElement { static get styles(): CSSResultGroup { return [ css` + ha-combo-box-item[disabled] { + background-color: var( + --mdc-text-field-disabled-fill-color, + whitesmoke + ); + } ha-combo-box-item { background-color: var(--mdc-text-field-fill-color, whitesmoke); border-radius: 4px; @@ -106,6 +112,12 @@ export class HaPickerField extends LitElement { } /* Add Similar focus style as the text field */ + ha-combo-box-item[disabled]:after { + background-color: var( + --mdc-text-field-disabled-line-color, + rgba(0, 0, 0, 0.42) + ); + } ha-combo-box-item:after { display: block; content: ""; From 228860a1ee268e0d1c11810159382345388c64d9 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Thu, 29 May 2025 11:05:26 +0300 Subject: [PATCH 019/216] Use theme variables for network graph labels (#25634) --- src/components/chart/ha-chart-base.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 2ecad752a3..37a12f07e6 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -494,6 +494,13 @@ export class HaChartBase extends LitElement { smooth: false, }, bar: { itemStyle: { barBorderWidth: 1.5 } }, + graph: { + label: { + color: style.getPropertyValue("--primary-text-color"), + textBorderColor: style.getPropertyValue("--primary-background-color"), + textBorderWidth: 2, + }, + }, categoryAxis: { axisLine: { show: false }, axisTick: { show: false }, From cd61725cf51f2ed78a190982cb4602744b0540c9 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Thu, 29 May 2025 15:29:24 +0300 Subject: [PATCH 020/216] Fix Z-WaveJS device count in dashboard (#25635) --- .../zwave_js/zwave_js-config-dashboard.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts index 15d2cf7926..aa4830a785 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts @@ -133,8 +133,11 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) { if (ERROR_STATES.includes(this._configEntry.state)) { return this._renderErrorScreen(); } + const provisioningDevices = + this._provisioningEntries?.filter((entry) => !entry.nodeId).length ?? 0; const notReadyDevices = - this._network?.controller.nodes.filter((node) => !node.ready).length ?? 0; + (this._network?.controller.nodes.filter((node) => !node.ready).length ?? + 0) + provisioningDevices; return html` 0 From 7ceba218faba6177160823dda2c2c53520cab45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 29 May 2025 10:59:58 +0300 Subject: [PATCH 021/216] Fix Zigbee capitalization in manage device button (#25637) --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 7fab97a767..316f78d7ec 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1782,7 +1782,7 @@ "buttons": { "add": "Add devices via this device", "remove": "Remove", - "manage": "Manage zigbee device", + "manage": "Manage Zigbee device", "reconfigure": "Reconfigure", "view_network": "View network" }, From e448268feb0799cc3c2015c4239a03fa2c2aaafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 29 May 2025 11:48:50 +0300 Subject: [PATCH 022/216] Spelling fixes (#25638) --- src/translations/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 316f78d7ec..0d2a4cc0e9 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1579,7 +1579,7 @@ "upload_in_progress": "A backup upload is currently in progress. The action will automatically proceed once the upload process is complete.", "restore_in_progress": "A backup restore is currently in progress. The action will automatically proceed once the restore process is complete.", "wait_for_backup": "Wait for the backup creation to finish", - "error_backup_state": "An error occured while getting the current backup state. Error: {error}", + "error_backup_state": "An error occurred while getting the current backup state. Error: {error}", "wait_for_upload": "Wait for backup upload to finish", "wait_for_restore": "Wait for backup restore to finish", "reload": { @@ -2378,7 +2378,7 @@ }, "generate": { "sync": { - "title": "Synchonization", + "title": "Synchronization", "name": "Backup name", "locations": "Locations", "locations_description": "What locations you want to automatically backup to.", From 560e3890b9158881c12773410e299e03b2d1cfc3 Mon Sep 17 00:00:00 2001 From: ildar170975 <71872483+ildar170975@users.noreply.github.com> Date: Fri, 30 May 2025 10:59:03 +0300 Subject: [PATCH 023/216] Revert #25027 "more-info-camera: disable download_snapshot if idle" (#25643) remove a check for "idle" --- src/dialogs/more-info/controls/more-info-camera.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-camera.ts b/src/dialogs/more-info/controls/more-info-camera.ts index e3842dc053..e45fe3a633 100644 --- a/src/dialogs/more-info/controls/more-info-camera.ts +++ b/src/dialogs/more-info/controls/more-info-camera.ts @@ -45,8 +45,7 @@ class MoreInfoCamera extends LitElement { ${this.hass.localize( "ui.dialogs.more_info_control.camera.download_snapshot" From 4c78eb4797776fb05dfeed0d9becfe68bf26dc05 Mon Sep 17 00:00:00 2001 From: ildar170975 <71872483+ildar170975@users.noreply.github.com> Date: Fri, 30 May 2025 08:38:45 +0300 Subject: [PATCH 024/216] Add cyrilic letters to slugify() (#25647) * add cyrilic * Update src/common/string/slugify.ts --------- Co-authored-by: Petar Petrov --- src/common/string/slugify.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/common/string/slugify.ts b/src/common/string/slugify.ts index b7ddfed77c..fe5e0f537c 100644 --- a/src/common/string/slugify.ts +++ b/src/common/string/slugify.ts @@ -1,9 +1,19 @@ // https://gist.github.com/hagemann/382adfc57adbd5af078dc93feef01fe1 export const slugify = (value: string, delimiter = "_") => { const a = - "àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìıİłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·"; - const b = `aaaaaaaaaacccddeeeeeeeegghiiiiiiiilmnnnnoooooooooprrsssssttuuuuuuuuuwxyyzzz${delimiter}`; + "àáâäæãåāăąабçćčđďдèéêëēėęěеёэфğǵгḧхîïíīįìıİийкłлḿмñńǹňнôöòóœøōõőоṕпŕřрßśšşșсťțтûüùúūǘůűųувẃẍÿýыžźżз·"; + const b = `aaaaaaaaaaabcccdddeeeeeeeeeeefggghhiiiiiiiiijkllmmnnnnnoooooooooopprrrsssssstttuuuuuuuuuuvwxyyyzzzz${delimiter}`; const p = new RegExp(a.split("").join("|"), "g"); + const complex_cyrillic = { + ж: "zh", + х: "kh", + ц: "ts", + ч: "ch", + ш: "sh", + щ: "shch", + ю: "iu", + я: "ia", + }; let slugified; @@ -14,6 +24,7 @@ export const slugify = (value: string, delimiter = "_") => { .toString() .toLowerCase() .replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters + .replace(/[а-я]/g, (c) => complex_cyrillic[c] || "") // Replace some cyrillic characters .replace(/(\d),(?=\d)/g, "$1") // Remove Commas between numbers .replace(/[^a-z0-9]+/g, delimiter) // Replace all non-word characters .replace(new RegExp(`(${delimiter})\\1+`, "g"), "$1") // Replace multiple delimiters with single delimiter From aebd6350c0cadeb114f46b0ec8750828dd50a26b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 30 May 2025 15:34:03 +0200 Subject: [PATCH 025/216] fix line height entity card (#25652) --- src/panels/lovelace/cards/hui-entity-card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index ae51480f36..3dbe834aee 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -301,7 +301,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - line-height: var(--ha-line-height-expanded); + line-height: var(--ha-line-height-condensed); } .value { From 74f9c1551ec0e2208044eb53ec42f0076009797f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 30 May 2025 16:47:54 +0200 Subject: [PATCH 026/216] Add label to collapse button in data table groups (#25653) --- src/components/data-table/ha-data-table.ts | 8 +++++--- src/translations/en.json | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index 2c3cfc28c7..b0cf4bab88 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -740,6 +740,7 @@ export class HaDataTable extends LitElement { }, {}); const groupedItems: DataTableRowData[] = []; Object.entries(sorted).forEach(([groupName, rows]) => { + const collapsed = collapsedGroups.includes(groupName); groupedItems.push({ append: true, selectable: false, @@ -751,9 +752,10 @@ export class HaDataTable extends LitElement { > ${groupName === UNDEFINED_GROUP_KEY diff --git a/src/translations/en.json b/src/translations/en.json index 0d2a4cc0e9..4068dc710c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -902,6 +902,8 @@ "hidden": "{number} hidden", "clear": "Clear", "ungrouped": "Ungrouped", + "collapse": "Collapse", + "expand": "Expand", "settings": { "header": "Customize", "hide": "Hide column {title}", From 84ac0cd41e3a429490e230f00b4b91857627b52a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 30 May 2025 16:48:15 +0200 Subject: [PATCH 027/216] Add css variables for start and end padding of tabs (#25654) --- src/panels/lovelace/hui-root.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 44ca521c0a..962a366767 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -1109,6 +1109,16 @@ class HUIRoot extends LitElement { sl-tab[aria-selected="true"] .edit-icon { display: inline-flex; } + sl-tab::part(base) { + padding-inline-start: var( + --ha-tab-padding-start, + var(--sl-spacing-large) + ); + padding-inline-end: var( + --ha-tab-padding-end, + var(--sl-spacing-large) + ); + } sl-tab::part(base) { padding-top: calc((var(--header-height) - 20px) / 2); padding-bottom: calc((var(--header-height) - 20px) / 2 - 2px); From 77eae6044d13683821d563cd89df175872e7c725 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Sat, 31 May 2025 16:15:54 +0200 Subject: [PATCH 028/216] Bumped version to 20250531.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0fc881abf4..6caa907986 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250528.0" +version = "20250531.0" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From 5daec6bbc5e1c8cde33324a23cefe76b26383f12 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Sun, 1 Jun 2025 08:39:10 +0200 Subject: [PATCH 029/216] Calendar add event button gap alignment (#25662) Calendar gap alignment --- src/panels/calendar/ha-full-calendar.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index 410aa4bdfd..17d83f1239 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -497,9 +497,9 @@ export class HAFullCalendar extends LitElement { ha-fab { position: absolute; - bottom: 32px; - right: 32px; - inset-inline-end: 32px; + bottom: 16px; + right: 16px; + inset-inline-end: 16px; inset-inline-start: initial; z-index: 1; } From 1483e8e38fd9fa21e678ab2d57092f616c9cd7a2 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 2 Jun 2025 16:02:06 +0200 Subject: [PATCH 030/216] Set answers to yes and no for cloud pipeline confirm (#25674) --- src/panels/config/cloud/login/cloud-login.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/panels/config/cloud/login/cloud-login.ts b/src/panels/config/cloud/login/cloud-login.ts index 8c42063569..456f96b34b 100644 --- a/src/panels/config/cloud/login/cloud-login.ts +++ b/src/panels/config/cloud/login/cloud-login.ts @@ -233,6 +233,8 @@ export class CloudLogin extends LitElement { text: this.hass.localize( "ui.panel.config.cloud.login.cloud_pipeline_text" ), + confirmText: this.hass.localize("ui.common.yes"), + dismissText: this.hass.localize("ui.common.no"), }) ) { setAssistPipelinePreferred(this.hass, result.cloud_pipeline); From 7dc4d555ae33c1f4e7786f25218d7cf8f244e2d1 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 3 Jun 2025 12:41:27 -0700 Subject: [PATCH 031/216] Hoist integration card tooltips (#25679) --- src/panels/config/integrations/ha-integration-card.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 9ac636c354..82d30ee3c1 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -163,6 +163,7 @@ export class HaIntegrationCard extends LitElement { : "custom"}" > itm.source === "system") ? html`
    Date: Thu, 5 Jun 2025 06:35:32 +0200 Subject: [PATCH 032/216] =?UTF-8?q?Adjust=20tooltip=20positioning=20in=20h?= =?UTF-8?q?a-sidebar=20for=20not=20first=20lis=E2=80=A6=20(#25696)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(tooltip): fix tooltip positioning in ha-sidebar for not first listbox --- src/components/ha-sidebar.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index ea303ab4c8..8ccaad0d95 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -626,12 +626,15 @@ class HaSidebar extends SubscribeMixin(LitElement) { this._tooltipHideTimeout = undefined; } const tooltip = this._tooltip; - const listbox = this.shadowRoot!.querySelector("ha-md-list")!; - let top = item.offsetTop + 11; - if (listbox.contains(item)) { - top += listbox.offsetTop; - top -= listbox.scrollTop; - } + const allListbox = this.shadowRoot!.querySelectorAll("ha-md-list")!; + const listbox = [...allListbox].find((lb) => lb.contains(item)); + + const top = + item.offsetTop + + 11 + + (listbox?.offsetTop ?? 0) - + (listbox?.scrollTop ?? 0); + tooltip.innerText = ( item.querySelector(".item-text") as HTMLElement ).innerText; From 89ce6870f6dab6c9c64a46645e7a13dfa49503a3 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 9 Jun 2025 14:45:23 +0300 Subject: [PATCH 033/216] Handle tiny values in a log chart (#25727) --- src/components/chart/state-history-chart-line.ts | 14 ++++++++++---- src/components/chart/statistics-chart.ts | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index 53cc3b926b..f8f7689539 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -229,14 +229,20 @@ export class StateHistoryChartLine extends LitElement { minYAxis = ({ min }) => Math.min(min, this.minYAxis!); } } else if (this.logarithmicScale) { - minYAxis = ({ min }) => Math.floor(min > 0 ? min * 0.95 : min * 1.05); + minYAxis = ({ min }) => { + const value = min > 0 ? min * 0.95 : min * 1.05; + return Math.abs(value) < 1 ? value : Math.floor(value); + }; } if (typeof maxYAxis === "number") { if (this.fitYData) { maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!); } } else if (this.logarithmicScale) { - maxYAxis = ({ max }) => Math.ceil(max > 0 ? max * 1.05 : max * 0.95); + maxYAxis = ({ max }) => { + const value = max > 0 ? max * 1.05 : max * 0.95; + return Math.abs(value) < 1 ? value : Math.ceil(value); + }; } this._chartOptions = { xAxis: { @@ -753,10 +759,10 @@ export class StateHistoryChartLine extends LitElement { if (this.logarithmicScale) { // log(0) is -Infinity, so we need to set a minimum value if (typeof value === "number") { - return Math.max(value, 0.1); + return Math.max(value, Number.EPSILON); } if (typeof value === "function") { - return (values: any) => Math.max(value(values), 0.1); + return (values: any) => Math.max(value(values), Number.EPSILON); } } return value; diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index ea8469653a..a1b7e30dc9 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -241,14 +241,20 @@ export class StatisticsChart extends LitElement { minYAxis = ({ min }) => Math.min(min, this.minYAxis!); } } else if (this.logarithmicScale) { - minYAxis = ({ min }) => Math.floor(min > 0 ? min * 0.95 : min * 1.05); + minYAxis = ({ min }) => { + const value = min > 0 ? min * 0.95 : min * 1.05; + return Math.abs(value) < 1 ? value : Math.floor(value); + }; } if (typeof maxYAxis === "number") { if (this.fitYData) { maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!); } } else if (this.logarithmicScale) { - maxYAxis = ({ max }) => Math.ceil(max > 0 ? max * 1.05 : max * 0.95); + maxYAxis = ({ max }) => { + const value = max > 0 ? max * 1.05 : max * 0.95; + return Math.abs(value) < 1 ? value : Math.ceil(value); + }; } const endTime = this.endTime ?? new Date(); let startTime = this.startTime; @@ -619,10 +625,10 @@ export class StatisticsChart extends LitElement { if (this.logarithmicScale) { // log(0) is -Infinity, so we need to set a minimum value if (typeof value === "number") { - return Math.max(value, 0.1); + return Math.max(value, Number.EPSILON); } if (typeof value === "function") { - return (values: any) => Math.max(value(values), 0.1); + return (values: any) => Math.max(value(values), Number.EPSILON); } } return value; From 8149ee60cb888ad2dd188bbec0ce026f190b811f Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 10 Jun 2025 11:28:51 +0300 Subject: [PATCH 034/216] Fix period boundaries in Energy dashboard (#25728) --- src/data/energy.ts | 4 +++- .../cards/energy/common/energy-chart-options.ts | 6 ++++++ .../lovelace/components/hui-energy-period-selector.ts | 11 ++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/data/energy.ts b/src/data/energy.ts index 904e39728d..0ee60fde90 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -679,7 +679,9 @@ export const getEnergyDataCollection = ( const period = preferredPeriod === "today" && hour === "0" ? "yesterday" : preferredPeriod; - [collection.start, collection.end] = calcDateRange(hass, period); + const [start, end] = calcDateRange(hass, period); + collection.start = calcDate(start, startOfDay, hass.locale, hass.config); + collection.end = calcDate(end, endOfDay, hass.locale, hass.config); const scheduleUpdatePeriod = () => { collection._updatePeriodTimeout = window.setTimeout( diff --git a/src/panels/lovelace/cards/energy/common/energy-chart-options.ts b/src/panels/lovelace/cards/energy/common/energy-chart-options.ts index d26d47f96d..ff423acec7 100644 --- a/src/panels/lovelace/cards/energy/common/energy-chart-options.ts +++ b/src/panels/lovelace/cards/energy/common/energy-chart-options.ts @@ -10,6 +10,8 @@ import { addYears, addMonths, addHours, + startOfDay, + addDays, } from "date-fns"; import type { BarSeriesOption, @@ -282,6 +284,10 @@ export function getCompareTransform(start: Date, compareStart?: Date) { ) { return (ts: Date) => addMonths(ts, compareMonthDiff); } + const compareDayDiff = differenceInDays(start, compareStart); + if (compareDayDiff !== 0 && start.getTime() === startOfDay(start).getTime()) { + return (ts: Date) => addDays(ts, compareDayDiff); + } const compareOffset = start.getTime() - compareStart.getTime(); return (ts: Date) => addMilliseconds(ts, compareOffset); } diff --git a/src/panels/lovelace/components/hui-energy-period-selector.ts b/src/panels/lovelace/components/hui-energy-period-selector.ts index 6f1742d031..22bf863e34 100644 --- a/src/panels/lovelace/components/hui-energy-period-selector.ts +++ b/src/panels/lovelace/components/hui-energy-period-selector.ts @@ -297,24 +297,17 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) { } private _dateRangeChanged(ev) { - const weekStartsOn = firstWeekdayIndex(this.hass.locale); this._startDate = calcDate( ev.detail.value.startDate, startOfDay, this.hass.locale, - this.hass.config, - { - weekStartsOn, - } + this.hass.config ); this._endDate = calcDate( ev.detail.value.endDate, endOfDay, this.hass.locale, - this.hass.config, - { - weekStartsOn, - } + this.hass.config ); this._updateCollectionPeriod(); From 2ab20aef69ecdc8aa153b69320180a4330d522bc Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 10 Jun 2025 12:20:10 +0200 Subject: [PATCH 035/216] Allow to open more info using query params (#25733) --- src/panels/lovelace/hui-root.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 962a366767..6f3d727d4d 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -57,6 +57,7 @@ import { showAlertDialog, showConfirmationDialog, } from "../../dialogs/generic/show-dialog-box"; +import { showMoreInfoDialog } from "../../dialogs/more-info/show-ha-more-info-dialog"; import { QuickBarMode, showQuickBar, @@ -75,9 +76,9 @@ import { getLovelaceStrategy } from "./strategies/get-strategy"; import { isLegacyStrategyConfig } from "./strategies/legacy-strategy"; import type { Lovelace } from "./types"; import "./views/hui-view"; -import "./views/hui-view-container"; import type { HUIView } from "./views/hui-view"; import "./views/hui-view-background"; +import "./views/hui-view-container"; @customElement("hui-root") class HUIRoot extends LitElement { @@ -490,7 +491,16 @@ class HUIRoot extends LitElement { } else if (searchParams.conversation === "1") { this._clearParam("conversation"); this._showVoiceCommandDialog(); + } else if (searchParams["more-info-entity-id"]) { + const entityId = searchParams["more-info-entity-id"]; + this._clearParam("more-info-entity-id"); + // Wait for the next render to ensure the view is fully loaded + // because the more info dialog is closed when the url changes + afterNextRender(() => { + this._showMoreInfoDialog(entityId); + }); } + window.addEventListener("scroll", this._handleWindowScroll, { passive: true, }); @@ -730,6 +740,10 @@ class HUIRoot extends LitElement { showVoiceCommandDialog(this, this.hass, { pipeline_id: "last_used" }); } + private _showMoreInfoDialog(entityId: string): void { + showMoreInfoDialog(this, { entityId }); + } + private _handleEnableEditMode(ev: CustomEvent): void { if (!shouldHandleRequestSelectedEvent(ev)) { return; From a26ca7d06529ec057750b8e24a7b1252c4def33d Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 10 Jun 2025 16:46:09 +0200 Subject: [PATCH 036/216] Fix custom value selected when clicking item in combo box (#25734) --- src/components/ha-combo-box.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/ha-combo-box.ts b/src/components/ha-combo-box.ts index 58ed2e1f51..ab5ecfe4c3 100644 --- a/src/components/ha-combo-box.ts +++ b/src/components/ha-combo-box.ts @@ -345,8 +345,10 @@ export class HaComboBox extends LitElement { // @ts-ignore this._comboBox._closeOnBlurIsPrevented = true; } + if (!this.opened) { + return; + } const newValue = ev.detail.value; - if (newValue !== this.value) { fireEvent(this, "value-changed", { value: newValue || undefined }); } From 28214aebc579dd2c67d34bd0d97bba0c342ec7c5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 10 Jun 2025 12:16:06 +0200 Subject: [PATCH 037/216] Reduce keypad gap and margin in alarm panel card (#25735) --- src/panels/lovelace/cards/hui-alarm-panel-card.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-alarm-panel-card.ts b/src/panels/lovelace/cards/hui-alarm-panel-card.ts index 17e55d852f..fe21e26c71 100644 --- a/src/panels/lovelace/cards/hui-alarm-panel-card.ts +++ b/src/panels/lovelace/cards/hui-alarm-panel-card.ts @@ -418,12 +418,11 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard { .keypad { --keypad-columns: 3; - margin-top: 12px; padding: 12px; display: grid; grid-template-columns: repeat(var(--keypad-columns), auto); grid-auto-rows: auto; - grid-gap: 24px; + grid-gap: 16px; justify-items: center; align-items: center; } From 4c5015e178f40799de2f204f3f6bd7b8b32355a5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 11 Jun 2025 12:04:54 +0200 Subject: [PATCH 038/216] Display full error for card preview mode (#25747) --- src/panels/lovelace/cards/hui-card.ts | 29 +++++++++++-- src/panels/lovelace/cards/hui-error-card.ts | 47 +++++++++++++-------- src/panels/lovelace/types.ts | 2 + 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index dd3bcbeb30..cba0e15aea 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -12,7 +12,8 @@ import { attachConditionMediaQueriesListeners, checkConditionsMet, } from "../common/validate-condition"; -import { createCardElement } from "../create-element/create-card-element"; +import { tryCreateCardElement } from "../create-element/create-card-element"; +import { createErrorCardElement } from "../create-element/create-element-base"; import type { LovelaceCard, LovelaceGridOptions } from "../types"; declare global { @@ -71,10 +72,23 @@ export class HuiCard extends ReactiveElement { public getGridOptions(): LovelaceGridOptions { const elementOptions = this.getElementGridOptions(); const configOptions = this.getConfigGridOptions(); - return { + const mergedConfig = { ...elementOptions, ...configOptions, }; + + // If the element has fixed rows or columns, we use the values from the element + if (elementOptions.fixed_rows) { + mergedConfig.rows = elementOptions.rows; + delete mergedConfig.min_rows; + delete mergedConfig.max_rows; + } + if (elementOptions.fixed_columns) { + mergedConfig.columns = elementOptions.columns; + delete mergedConfig.min_columns; + delete mergedConfig.max_columns; + } + return mergedConfig; } // options provided by the element @@ -119,7 +133,15 @@ export class HuiCard extends ReactiveElement { } private _loadElement(config: LovelaceCardConfig) { - this._element = createCardElement(config); + try { + this._element = tryCreateCardElement(config); + } catch (err: unknown) { + const errorMessage = err instanceof Error ? err.message : undefined; + this._element = createErrorCardElement({ + type: "error", + message: errorMessage, + }); + } this._elementConfig = config; if (this.hass) { this._element.hass = this.hass; @@ -200,6 +222,7 @@ export class HuiCard extends ReactiveElement { this._element.preview = this.preview; // For backwards compatibility (this._element as any).editMode = this.preview; + fireEvent(this, "card-updated"); } catch (e: any) { // eslint-disable-next-line no-console console.error(this.config?.type, e); diff --git a/src/panels/lovelace/cards/hui-error-card.ts b/src/panels/lovelace/cards/hui-error-card.ts index 837f78e367..26d72284d6 100644 --- a/src/panels/lovelace/cards/hui-error-card.ts +++ b/src/panels/lovelace/cards/hui-error-card.ts @@ -1,11 +1,11 @@ +import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js"; +import "../../../components/ha-card"; +import "../../../components/ha-svg-icon"; import type { HomeAssistant } from "../../../types"; import type { LovelaceCard, LovelaceGridOptions } from "../types"; import type { ErrorCardConfig } from "./types"; -import "../../../components/ha-card"; -import "../../../components/ha-svg-icon"; const ERROR_ICONS = { warning: mdiAlertOutline, @@ -30,9 +30,10 @@ export class HuiErrorCard extends LitElement implements LovelaceCard { public getGridOptions(): LovelaceGridOptions { return { columns: 6, - rows: 1, + rows: this.preview ? "auto" : 1, min_rows: 1, min_columns: 6, + fixed_rows: this.preview, }; } @@ -45,17 +46,24 @@ export class HuiErrorCard extends LitElement implements LovelaceCard { const error = this._config?.error || this.hass?.localize("ui.errors.config.configuration_error"); - const showTitle = this.hass === undefined || this.hass?.user?.is_admin; + const showTitle = + this.hass === undefined || this.hass?.user?.is_admin || this.preview; + const showMessage = this.preview; return html` -
    - - - +
    +
    + + + +
    + ${showTitle + ? html`
    ${error}
    ` + : nothing}
    - ${showTitle - ? html`
    ${error}
    ` + ${showMessage && this._config?.message + ? html`
    ${this._config.message}
    ` : nothing} `; @@ -65,10 +73,6 @@ export class HuiErrorCard extends LitElement implements LovelaceCard { ha-card { height: 100%; border-width: 0; - display: flex; - align-items: center; - column-gap: 16px; - padding: 16px; } ha-card::after { position: absolute; @@ -81,6 +85,15 @@ export class HuiErrorCard extends LitElement implements LovelaceCard { content: ""; border-radius: var(--ha-card-border-radius, 12px); } + .header { + display: flex; + align-items: center; + gap: 8px; + padding: 16px; + } + .message { + padding: 0 16px 16px 16px; + } .no-title { justify-content: center; } @@ -90,13 +103,13 @@ export class HuiErrorCard extends LitElement implements LovelaceCard { text-overflow: ellipsis; font-weight: var(--ha-font-weight-bold); } - ha-card.warning > .icon { + ha-card.warning .icon { color: var(--warning-color); } ha-card.warning::after { background-color: var(--warning-color); } - ha-card.error > .icon { + ha-card.error .icon { color: var(--error-color); } ha-card.error::after { diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index 8da041bb27..40534349bf 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -62,6 +62,8 @@ export interface LovelaceGridOptions { min_columns?: number; min_rows?: number; max_rows?: number; + fixed_rows?: boolean; + fixed_columns?: boolean; } export interface LovelaceCard extends HTMLElement { From 0d979625783264879cf67a99eef75a177a7d75ed Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 11 Jun 2025 14:30:59 +0200 Subject: [PATCH 039/216] Bumped version to 20250531.1 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6caa907986..762f3468b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250531.0" +version = "20250531.1" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From 89d0746c7cdd864269ef28cf8aaa1b81da55f3c5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 11 Jun 2025 16:15:50 +0200 Subject: [PATCH 040/216] Fix edit card not working in chrome after editing (#25751) --- src/panels/lovelace/components/hui-badge-edit-mode.ts | 2 +- src/panels/lovelace/components/hui-card-edit-mode.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/components/hui-badge-edit-mode.ts b/src/panels/lovelace/components/hui-badge-edit-mode.ts index c765b3e8a5..521c47fff9 100644 --- a/src/panels/lovelace/components/hui-badge-edit-mode.ts +++ b/src/panels/lovelace/components/hui-badge-edit-mode.ts @@ -85,7 +85,7 @@ export class HuiBadgeEditMode extends LitElement { if (this._touchStarted) return; this._hover = true; }); - this.addEventListener("mouseout", () => { + this.addEventListener("mouseleave", () => { this._hover = false; }); this.addEventListener("click", () => { diff --git a/src/panels/lovelace/components/hui-card-edit-mode.ts b/src/panels/lovelace/components/hui-card-edit-mode.ts index 0711cf5849..d42fc1590d 100644 --- a/src/panels/lovelace/components/hui-card-edit-mode.ts +++ b/src/panels/lovelace/components/hui-card-edit-mode.ts @@ -71,7 +71,7 @@ export class HuiCardEditMode extends LitElement { if (this._touchStarted) return; this._hover = true; }); - this.addEventListener("mouseout", () => { + this.addEventListener("mouseleave", () => { this._hover = false; }); this.addEventListener("click", () => { From bffe38c827a737e46bd8b3a7c8bb9bbb264c842b Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 11 Jun 2025 16:26:37 +0200 Subject: [PATCH 041/216] Bumped version to 20250531.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 762f3468b9..c7a4e50fc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250531.1" +version = "20250531.2" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From 7f68447a4fae7e9c1ad43a05367aa4b93c4a73ed Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Thu, 12 Jun 2025 10:40:36 +0300 Subject: [PATCH 042/216] Fix alerts refresh on device page (#25748) * Fix alerts refresh on device page * don't reset actions periodically * reset stuff only on deviceId change --- .../config/devices/ha-config-device-page.ts | 76 ++++++++----------- 1 file changed, 32 insertions(+), 44 deletions(-) diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index e396c7072b..44530ab333 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -127,16 +127,15 @@ export class HaConfigDevicePage extends LitElement { @state() private _related?: RelatedResult; - // If a number, it's the request ID so we make sure we don't show older info - @state() private _diagnosticDownloadLinks?: number | DeviceAction[]; + @state() private _diagnosticDownloadLinks: DeviceAction[] = []; - @state() private _deleteButtons?: DeviceAction[]; + @state() private _deleteButtons: DeviceAction[] = []; - @state() private _deviceActions?: DeviceAction[]; + @state() private _deviceActions: DeviceAction[] = []; - @state() private _deviceAlerts?: DeviceAlert[]; + @state() private _deviceAlerts: DeviceAlert[] = []; - private _deviceAlertsTimeout?: number; + private _deviceAlertsActionsTimeout?: number; @state() @consume({ context: fullEntitiesContext, subscribe: true }) @@ -255,42 +254,19 @@ export class HaConfigDevicePage extends LitElement { public willUpdate(changedProps) { super.willUpdate(changedProps); - if ( - changedProps.has("deviceId") || - changedProps.has("devices") || - changedProps.has("entries") - ) { - this._diagnosticDownloadLinks = undefined; - this._deleteButtons = undefined; - this._deviceActions = undefined; - this._deviceAlerts = undefined; + if (changedProps.has("deviceId") || changedProps.has("entries")) { + this._deviceActions = []; + this._deviceAlerts = []; + this._deleteButtons = []; + this._diagnosticDownloadLinks = []; + this._fetchData(); } - - if ( - (this._diagnosticDownloadLinks && - this._deleteButtons && - this._deviceActions && - this._deviceAlerts) || - !this.deviceId || - !this.entries - ) { - return; - } - - this._diagnosticDownloadLinks = Math.random(); - this._deleteButtons = []; // To prevent re-rendering if no delete buttons - this._deviceActions = []; - this._deviceAlerts = []; - this._getDiagnosticButtons(this._diagnosticDownloadLinks); - this._getDeleteActions(); - this._getDeviceActions(); - clearTimeout(this._deviceAlertsTimeout); - this._getDeviceAlerts(); } protected firstUpdated(changedProps) { super.firstUpdated(changedProps); loadDeviceRegistryDetailDialog(); + this._fetchData(); } protected updated(changedProps) { @@ -302,7 +278,7 @@ export class HaConfigDevicePage extends LitElement { public disconnectedCallback() { super.disconnectedCallback(); - clearTimeout(this._deviceAlertsTimeout); + clearTimeout(this._deviceAlertsActionsTimeout); } protected render() { @@ -909,7 +885,18 @@ export class HaConfigDevicePage extends LitElement { `; } - private async _getDiagnosticButtons(requestId: number): Promise { + private _fetchData() { + if (this.deviceId && this.entries.length) { + this._getDiagnosticButtons(); + this._getDeleteActions(); + clearTimeout(this._deviceAlertsActionsTimeout); + this._getDeviceActions(); + this._getDeviceAlerts(); + } + } + + private async _getDiagnosticButtons(): Promise { + const deviceId = this.deviceId; if (!isComponentLoaded(this.hass, "diagnostics")) { return; } @@ -951,7 +938,8 @@ export class HaConfigDevicePage extends LitElement { links = links.filter(Boolean); - if (this._diagnosticDownloadLinks !== requestId) { + if (this.deviceId !== deviceId) { + // abort if the device has changed return; } if (links.length > 0) { @@ -1176,12 +1164,12 @@ export class HaConfigDevicePage extends LitElement { deviceAlerts.push(...alerts); } + this._deviceAlerts = deviceAlerts; if (deviceAlerts.length) { - this._deviceAlerts = deviceAlerts; - this._deviceAlertsTimeout = window.setTimeout( - () => this._getDeviceAlerts(), - DEVICE_ALERTS_INTERVAL - ); + this._deviceAlertsActionsTimeout = window.setTimeout(() => { + this._getDeviceAlerts(); + this._getDeviceActions(); + }, DEVICE_ALERTS_INTERVAL); } } From 8cead75087a93b39e45805da5561c4de75cc3458 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 12 Jun 2025 09:29:58 +0200 Subject: [PATCH 043/216] Change backup type order (#25759) --- src/data/backup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/backup.ts b/src/data/backup.ts index 12261eb810..fd0324a229 100644 --- a/src/data/backup.ts +++ b/src/data/backup.ts @@ -339,7 +339,7 @@ export const computeBackupSize = (backup: BackupContent) => export type BackupType = "automatic" | "manual" | "addon_update"; -const BACKUP_TYPE_ORDER: BackupType[] = ["automatic", "manual", "addon_update"]; +const BACKUP_TYPE_ORDER: BackupType[] = ["automatic", "addon_update", "manual"]; export const getBackupTypes = memoize((isHassio: boolean) => isHassio From 38545a01dd202e6c51561e28f4d1c4798ba0358c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 12 Jun 2025 09:44:20 +0200 Subject: [PATCH 044/216] Ensure grid options always return an object (#25760) --- src/panels/lovelace/cards/hui-card.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index cba0e15aea..271e2cd027 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -96,7 +96,9 @@ export class HuiCard extends ReactiveElement { if (!this._element) return {}; if (this._element.getGridOptions) { - return this._element.getGridOptions(); + const options = this._element.getGridOptions(); + // Some custom cards might return undefined, so we ensure we return an object + return options || {}; } if (this._element.getLayoutOptions) { // Disabled for now to avoid spamming the console, need to be re-enabled when hui-card performance are fixed From 359460b570ca717a73f54e97d8fbd68d3acb7bf2 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 12 Jun 2025 14:22:11 +0200 Subject: [PATCH 045/216] Bumped version to 20250531.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c7a4e50fc4..59884f9957 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250531.2" +version = "20250531.3" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From d7f4a7acb0cf1ecbfe47ad5293d0504013869f34 Mon Sep 17 00:00:00 2001 From: Anthony Relle Date: Tue, 17 Jun 2025 10:54:34 +0100 Subject: [PATCH 046/216] Update ElectricityMaps URL in Energy Dashboard (#25816) fix: update electricitymap domain --- .../lovelace/cards/energy/hui-energy-distribution-card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts index d2cb3af1c4..60a0a801db 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts @@ -205,7 +205,7 @@ class HuiEnergyDistrubutionCard let homeHighCarbonCircumference: number | undefined; // This fallback is used in the demo - let electricityMapUrl = "https://app.electricitymap.org"; + let electricityMapUrl = "https://app.electricitymaps.com"; if ( hasGrid && From bdf48140e40e2158280b71f303496c1426824824 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 17 Jun 2025 16:07:57 +0300 Subject: [PATCH 047/216] Reduce reset-zoom button size on timeline charts (#25796) --- src/components/chart/ha-chart-base.ts | 14 +++++++++++++- .../chart/state-history-chart-timeline.ts | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 37a12f07e6..b40256b60b 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -49,6 +49,9 @@ export class HaChartBase extends LitElement { @property({ attribute: "expand-legend", type: Boolean }) public expandLegend?: boolean; + @property({ attribute: "small-controls", type: Boolean }) + public smallControls?: boolean; + // extraComponents is not reactive and should not trigger updates public extraComponents?: any[]; @@ -194,7 +197,7 @@ export class HaChartBase extends LitElement {
    ${this._renderLegend()} -
    +
    ${this._isZoomed ? html` `; From cc0586bf36d7a66c731a3ff07873f0c156d86542 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 17 Jun 2025 15:14:07 +0200 Subject: [PATCH 048/216] Fix disabled color in dark mode in production (#25818) --- src/common/style/derived-css-vars.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common/style/derived-css-vars.ts b/src/common/style/derived-css-vars.ts index 65e6f21d06..3b2add7e1b 100644 --- a/src/common/style/derived-css-vars.ts +++ b/src/common/style/derived-css-vars.ts @@ -9,7 +9,9 @@ const _extractCssVars = ( cssString.split(";").forEach((rawLine) => { const line = rawLine.substring(rawLine.indexOf("--")).trim(); if (line.startsWith("--") && condition(line)) { - const [name, value] = line.split(":").map((part) => part.trim()); + const [name, value] = line + .split(":") + .map((part) => part.replaceAll("}", "").trim()); variables[name.substring(2, name.length)] = value; } }); @@ -25,7 +27,10 @@ export const extractVar = (css: CSSResult, varName: string) => { } const endIndex = cssString.indexOf(";", startIndex + search.length); - return cssString.substring(startIndex + search.length, endIndex).trim(); + return cssString + .substring(startIndex + search.length, endIndex) + .replaceAll("}", "") + .trim(); }; export const extractVars = (css: CSSResult) => { From 3cfc6297b5fa33e2e76ec6f9ca5a100f19b74498 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 17 Jun 2025 16:14:33 +0300 Subject: [PATCH 049/216] Fix sankey total calculation to account for `included_in_stat` (#25805) --- src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts index da162673e3..5f1a9cf146 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts @@ -238,7 +238,6 @@ class HuiEnergySankeyCard if (value < 0.01) { return; } - untrackedConsumption -= value; const node = { id: device.stat_consumption, label: @@ -260,6 +259,8 @@ class HuiEnergySankeyCard source: node.parent, target: node.id, }); + } else { + untrackedConsumption -= value; } deviceNodes.push(node); }); From 4f8c50aaa99ad5b1bb4f94e2e9d27d1224f22536 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 17 Jun 2025 09:32:27 -0400 Subject: [PATCH 050/216] Show voice ID in Cloud Pref (#25809) * Show voice ID in Cloud Pref * Address style comments * Update src/panels/config/cloud/account/cloud-tts-pref.ts --------- Co-authored-by: Bram Kragten --- .../config/cloud/account/cloud-account.ts | 1 + .../config/cloud/account/cloud-tts-pref.ts | 61 ++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/panels/config/cloud/account/cloud-account.ts b/src/panels/config/cloud/account/cloud-account.ts index 82730ce21c..dbbf4cb611 100644 --- a/src/panels/config/cloud/account/cloud-account.ts +++ b/src/panels/config/cloud/account/cloud-account.ts @@ -204,6 +204,7 @@ export class CloudAccount extends SubscribeMixin(LitElement) { diff --git a/src/panels/config/cloud/account/cloud-tts-pref.ts b/src/panels/config/cloud/account/cloud-tts-pref.ts index 8adc58e737..59370ae919 100644 --- a/src/panels/config/cloud/account/cloud-tts-pref.ts +++ b/src/panels/config/cloud/account/cloud-tts-pref.ts @@ -1,6 +1,7 @@ import "@material/mwc-button"; import { css, html, LitElement, nothing } from "lit"; +import { mdiContentCopy } from "@mdi/js"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../common/dom/fire_event"; @@ -20,6 +21,8 @@ import { import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../../types"; import { showTryTtsDialog } from "./show-dialog-cloud-tts-try"; +import { copyToClipboard } from "../../../../common/util/copy-clipboard"; +import { showToast } from "../../../../util/toast"; export const getCloudTtsSupportedVoices = ( language: string, @@ -46,6 +49,8 @@ export class CloudTTSPref extends LitElement { @property({ attribute: false }) public cloudStatus?: CloudStatusLoggedIn; + @property({ type: Boolean, reflect: true }) public narrow = false; + @state() private savingPreferences = false; @state() private ttsInfo?: CloudTTSInfo; @@ -103,6 +108,25 @@ export class CloudTTSPref extends LitElement {
    +
    +
    + ${this.hass.localize( + "ui.components.media-browser.tts.selected_voice_id" + )} +
    + ${defaultVoice[1]} + ${this.narrow + ? nothing + : html` + + `} +
    +
    ${this.hass.localize("ui.panel.config.cloud.account.tts.try")} @@ -196,6 +220,14 @@ export class CloudTTSPref extends LitElement { } } + private async _copyVoiceId(ev) { + ev.preventDefault(); + await copyToClipboard(this.cloudStatus!.prefs.tts_default_voice[1]); + showToast(this, { + message: this.hass.localize("ui.common.copied_clipboard"), + }); + } + static styles = css` a { color: var(--primary-color); @@ -226,7 +258,34 @@ export class CloudTTSPref extends LitElement { } .card-actions { display: flex; - flex-direction: row-reverse; + align-items: center; + } + code { + margin-left: 6px; + font-weight: var(--ha-font-weight-bold); + } + .voice-id { + display: flex; + align-items: center; + font-size: var(--ha-font-size-s); + color: var(--secondary-text-color); + --mdc-icon-size: 14px; + --mdc-icon-button-size: 24px; + } + :host([narrow]) .voice-id { + flex-direction: column; + font-size: var(--ha-font-size-xs); + align-items: start; + align-items: left; + } + :host([narrow]) .label { + text-transform: uppercase; + } + :host([narrow]) code { + margin-left: 0; + } + .flex { + flex: 1; } `; } From 9c9ce78ff9b6023e2e98b9b4d7c7a06d36df4aca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:05:09 +0200 Subject: [PATCH 051/216] Update dependency lint-staged to v16.1.1 (#25817) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0734945705..062b57081d 100644 --- a/package.json +++ b/package.json @@ -204,7 +204,7 @@ "husky": "9.1.7", "jsdom": "26.1.0", "jszip": "3.10.1", - "lint-staged": "16.1.0", + "lint-staged": "16.1.1", "lit-analyzer": "2.0.3", "lodash.merge": "4.6.2", "lodash.template": "4.5.0", diff --git a/yarn.lock b/yarn.lock index 4c33c8fa3b..17e558dfc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9377,7 +9377,7 @@ __metadata: leaflet: "npm:1.9.4" leaflet-draw: "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch" leaflet.markercluster: "npm:1.5.3" - lint-staged: "npm:16.1.0" + lint-staged: "npm:16.1.1" lit: "npm:3.3.0" lit-analyzer: "npm:2.0.3" lit-html: "npm:3.3.0" @@ -10750,9 +10750,9 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:16.1.0": - version: 16.1.0 - resolution: "lint-staged@npm:16.1.0" +"lint-staged@npm:16.1.1": + version: 16.1.1 + resolution: "lint-staged@npm:16.1.1" dependencies: chalk: "npm:^5.4.1" commander: "npm:^14.0.0" @@ -10766,7 +10766,7 @@ __metadata: yaml: "npm:^2.8.0" bin: lint-staged: bin/lint-staged.js - checksum: 10/c7a52ac9551f284b09d389d515ee0951055e13f71aa18990e0804fb8738d50763aa6e262879a4f0f1cf376a4c1772748f6782e8fe98a4cb322b168af16711ba6 + checksum: 10/f0fe2251d8560f9dca0529268a29202591c2733fb51c69acc89552341939cd1f0d96653f61f7110a68fe0eff0ebfda45f2da887c533220ce5cd95e22f042b6da languageName: node linkType: hard From 0ddf3aa675c63d8d2e67835f6b614976b45e396b Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 17 Jun 2025 12:12:50 -0700 Subject: [PATCH 052/216] Uncap width of ha-statistics-picker (#25822) --- src/components/entity/ha-statistics-picker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/entity/ha-statistics-picker.ts b/src/components/entity/ha-statistics-picker.ts index e259a222e9..96eab269ee 100644 --- a/src/components/entity/ha-statistics-picker.ts +++ b/src/components/entity/ha-statistics-picker.ts @@ -173,7 +173,6 @@ class HaStatisticsPicker extends LitElement { static styles = css` :host { - width: 200px; display: block; } ha-statistic-picker { From e3cdd698355def806de2b457a9cfd109757228b7 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 17 Jun 2025 23:32:06 -0700 Subject: [PATCH 053/216] Display yaml errors in all ha-yaml-editor (#25819) --- src/components/ha-yaml-editor.ts | 22 +++++++++++++++++++ .../config/automation/ha-automation-editor.ts | 1 + src/panels/config/scene/ha-scene-editor.ts | 1 + src/panels/config/script/ha-script-editor.ts | 1 + .../lovelace/editor/hui-element-editor.ts | 1 + 5 files changed, 26 insertions(+) diff --git a/src/components/ha-yaml-editor.ts b/src/components/ha-yaml-editor.ts index 561feb0f3f..c2a95a4022 100644 --- a/src/components/ha-yaml-editor.ts +++ b/src/components/ha-yaml-editor.ts @@ -11,6 +11,7 @@ import { showToast } from "../util/toast"; import { copyToClipboard } from "../common/util/copy-clipboard"; import type { HaCodeEditor } from "./ha-code-editor"; import "./ha-button"; +import "./ha-alert"; const isEmpty = (obj: Record): boolean => { if (typeof obj !== "object" || obj === null) { @@ -51,8 +52,15 @@ export class HaYamlEditor extends LitElement { @property({ attribute: "has-extra-actions", type: Boolean }) public hasExtraActions = false; + @property({ attribute: "show-errors", type: Boolean }) + public showErrors = true; + @state() private _yaml = ""; + @state() private _error = ""; + + @state() private _showingError = false; + @query("ha-code-editor") _codeEditor?: HaCodeEditor; public setValue(value): void { @@ -107,8 +115,12 @@ export class HaYamlEditor extends LitElement { autocomplete-icons .error=${this.isValid === false} @value-changed=${this._onChange} + @blur=${this._onBlur} dir="ltr" > + ${this._showingError + ? html`${this._error}` + : nothing} ${this.copyClipboard || this.hasExtraActions ? html`
    @@ -146,6 +158,10 @@ export class HaYamlEditor extends LitElement { } else { parsed = {}; } + this._error = errorMsg ?? ""; + if (isValid) { + this._showingError = false; + } this.value = parsed; this.isValid = isValid; @@ -157,6 +173,12 @@ export class HaYamlEditor extends LitElement { } as any); } + private _onBlur(): void { + if (this.showErrors && this._error) { + this._showingError = true; + } + } + get yaml() { return this._yaml; } diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 5fae94ede3..09913aec3c 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -501,6 +501,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin( .defaultValue=${this._preprocessYaml()} .readOnly=${this._readOnly} @value-changed=${this._yamlChanged} + .showErrors=${false} >` : nothing}
    diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index d118cbf9c9..66494fbf3e 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -320,6 +320,7 @@ export class HaSceneEditor extends PreventUnsavedMixin( .hass=${this.hass} .defaultValue=${this._config} @value-changed=${this._yamlChanged} + .showErrors=${false} >`; } diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index 0e422ae834..4eaffe3d45 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -441,6 +441,7 @@ export class HaScriptEditor extends SubscribeMixin( .defaultValue=${this._preprocessYaml()} .readOnly=${this._readOnly} @value-changed=${this._yamlChanged} + .showErrors=${false} >` : nothing}
    diff --git a/src/panels/lovelace/editor/hui-element-editor.ts b/src/panels/lovelace/editor/hui-element-editor.ts index 9ac4546bab..b8a21bb35f 100644 --- a/src/panels/lovelace/editor/hui-element-editor.ts +++ b/src/panels/lovelace/editor/hui-element-editor.ts @@ -243,6 +243,7 @@ export abstract class HuiElementEditor< @blur=${this._onBlurYaml} @keydown=${this._ignoreKeydown} dir="ltr" + .showErrors=${false} >
    `} From 98dcce6af15ade8f38a5b26657058bf33321842d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:24:40 +0200 Subject: [PATCH 054/216] Update dependency lint-staged to v16.1.2 (#25833) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 062b57081d..b1a5586c5d 100644 --- a/package.json +++ b/package.json @@ -204,7 +204,7 @@ "husky": "9.1.7", "jsdom": "26.1.0", "jszip": "3.10.1", - "lint-staged": "16.1.1", + "lint-staged": "16.1.2", "lit-analyzer": "2.0.3", "lodash.merge": "4.6.2", "lodash.template": "4.5.0", diff --git a/yarn.lock b/yarn.lock index 17e558dfc8..5b3f9ca7ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9377,7 +9377,7 @@ __metadata: leaflet: "npm:1.9.4" leaflet-draw: "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch" leaflet.markercluster: "npm:1.5.3" - lint-staged: "npm:16.1.1" + lint-staged: "npm:16.1.2" lit: "npm:3.3.0" lit-analyzer: "npm:2.0.3" lit-html: "npm:3.3.0" @@ -10750,9 +10750,9 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:16.1.1": - version: 16.1.1 - resolution: "lint-staged@npm:16.1.1" +"lint-staged@npm:16.1.2": + version: 16.1.2 + resolution: "lint-staged@npm:16.1.2" dependencies: chalk: "npm:^5.4.1" commander: "npm:^14.0.0" @@ -10766,7 +10766,7 @@ __metadata: yaml: "npm:^2.8.0" bin: lint-staged: bin/lint-staged.js - checksum: 10/f0fe2251d8560f9dca0529268a29202591c2733fb51c69acc89552341939cd1f0d96653f61f7110a68fe0eff0ebfda45f2da887c533220ce5cd95e22f042b6da + checksum: 10/90df77c2f59cdc5ebeb8a60767f07025a8aed9161f604fea6cf1ca895ff3b56995a00145a3e0b5c0bf22e8f667a6182256b68e001e5f3118e46a3c5150bede82 languageName: node linkType: hard From 723bb4dfeb053ed70be09517d62d66bafe6d10d8 Mon Sep 17 00:00:00 2001 From: Bastian Date: Wed, 18 Jun 2025 17:31:15 +0200 Subject: [PATCH 055/216] Change duration input to ha-time-input component (#25800) * Change duration input to ha-time-input component * Change ha-time-input to duration-input component * Add DurationDict to ForDict cast --- .../config/helpers/forms/ha-timer-form.ts | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/panels/config/helpers/forms/ha-timer-form.ts b/src/panels/config/helpers/forms/ha-timer-form.ts index d5d8a43762..236681051d 100644 --- a/src/panels/config/helpers/forms/ha-timer-form.ts +++ b/src/panels/config/helpers/forms/ha-timer-form.ts @@ -5,10 +5,14 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-checkbox"; import "../../../../components/ha-formfield"; import "../../../../components/ha-icon-picker"; +import "../../../../components/ha-duration-input"; import "../../../../components/ha-textfield"; import type { DurationDict, Timer } from "../../../../data/timer"; import { haStyle } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; +import { createDurationData } from "../../../../common/datetime/create_duration_data"; +import type { HaDurationData } from "../../../../components/ha-duration-input"; +import type { ForDict } from "../../../../data/automation"; @customElement("ha-timer-form") class HaTimerForm extends LitElement { @@ -24,6 +28,8 @@ class HaTimerForm extends LitElement { @state() private _duration!: string | number | DurationDict; + @state() private _duration_data!: HaDurationData | undefined; + @state() private _restore!: boolean; set item(item: Timer) { @@ -39,6 +45,8 @@ class HaTimerForm extends LitElement { this._duration = "00:00:00"; this._restore = false; } + + this._setDurationData(); } public focus() { @@ -79,14 +87,11 @@ class HaTimerForm extends LitElement { "ui.dialogs.helper_settings.generic.icon" )} > - + .data=${this._duration_data} + @value-changed=${this._valueChanged} + > Date: Wed, 18 Jun 2025 17:44:27 +0200 Subject: [PATCH 056/216] Remove debug type in secondary line in statistic picker (#25835) --- src/components/entity/ha-statistic-picker.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/entity/ha-statistic-picker.ts b/src/components/entity/ha-statistic-picker.ts index 3d20ab7a43..24f96b0eb2 100644 --- a/src/components/entity/ha-statistic-picker.ts +++ b/src/components/entity/ha-statistic-picker.ts @@ -438,10 +438,8 @@ export class HaStatisticPicker extends LitElement { ` : nothing} ${item.primary} - ${item.secondary || item.type - ? html`${item.secondary} - ${item.type}` + ${item.secondary + ? html`${item.secondary}` : nothing} ${item.statistic_id && showEntityId ? html` From cf03e041a86f4ef77a2af310760405e7ec9be5f5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 18 Jun 2025 17:57:11 +0200 Subject: [PATCH 057/216] Fix combobox helper (#25834) * Fix combobox helper * Pass disabled to all helpers --- src/components/ha-base-time-input.ts | 4 +++- src/components/ha-combo-box.ts | 15 +++++++++++++-- src/components/ha-form/ha-form-integer.ts | 4 +++- src/components/ha-input-helper-text.ts | 5 +++++ src/components/ha-labeled-slider.ts | 4 +++- .../ha-selector/ha-selector-datetime.ts | 4 +++- src/components/ha-selector/ha-selector-number.ts | 4 +++- src/components/ha-selector/ha-selector-object.ts | 4 +++- src/components/ha-selector/ha-selector-select.ts | 4 +++- .../ha-selector/ha-selector-template.ts | 4 +++- src/components/ha-target-picker.ts | 4 +++- 11 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/components/ha-base-time-input.ts b/src/components/ha-base-time-input.ts index eae4ccc306..674d36ca49 100644 --- a/src/components/ha-base-time-input.ts +++ b/src/components/ha-base-time-input.ts @@ -271,7 +271,9 @@ export class HaBaseTimeInput extends LitElement { `} ${this.helper - ? html`${this.helper}` + ? html`${this.helper}` : nothing} `; } diff --git a/src/components/ha-combo-box.ts b/src/components/ha-combo-box.ts index ab5ecfe4c3..bcdc9077e7 100644 --- a/src/components/ha-combo-box.ts +++ b/src/components/ha-combo-box.ts @@ -19,6 +19,7 @@ import type { HomeAssistant } from "../types"; import "./ha-combo-box-item"; import "./ha-combo-box-textfield"; import "./ha-icon-button"; +import "./ha-input-helper-text"; import "./ha-textfield"; import type { HaTextField } from "./ha-textfield"; @@ -195,8 +196,6 @@ export class HaComboBox extends LitElement { >`} .icon=${this.icon} .invalid=${this.invalid} - .helper=${this.helper} - helperPersistent .disableSetValue=${this._disableSetValue} > @@ -222,9 +221,18 @@ export class HaComboBox extends LitElement { @click=${this._toggleOpen} > + ${this._renderHelper()} `; } + private _renderHelper() { + return this.helper + ? html`${this.helper}` + : ""; + } + private _defaultRowRenderer: ComboBoxLitRenderer< string | Record > = (item) => html` @@ -398,6 +406,9 @@ export class HaComboBox extends LitElement { inset-inline-end: 36px; direction: var(--direction); } + ha-input-helper-text { + margin-top: 4px; + } `; } diff --git a/src/components/ha-form/ha-form-integer.ts b/src/components/ha-form/ha-form-integer.ts index 777056c184..9bcd93d3be 100644 --- a/src/components/ha-form/ha-form-integer.ts +++ b/src/components/ha-form/ha-form-integer.ts @@ -71,7 +71,9 @@ export class HaFormInteger extends LitElement implements HaFormElement { > ${this.helper - ? html`${this.helper}` + ? html`${this.helper}` : ""} `; diff --git a/src/components/ha-input-helper-text.ts b/src/components/ha-input-helper-text.ts index 6d817d1bf6..86ba8cb280 100644 --- a/src/components/ha-input-helper-text.ts +++ b/src/components/ha-input-helper-text.ts @@ -19,6 +19,11 @@ class InputHelperText extends LitElement { padding-right: 16px; padding-inline-start: 16px; padding-inline-end: 16px; + letter-spacing: var( + --mdc-typography-caption-letter-spacing, + 0.0333333333em + ); + line-height: normal; } :host([disabled]) { color: var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.6)); diff --git a/src/components/ha-labeled-slider.ts b/src/components/ha-labeled-slider.ts index 483d5aefd6..7c473fc5c1 100644 --- a/src/components/ha-labeled-slider.ts +++ b/src/components/ha-labeled-slider.ts @@ -47,7 +47,9 @@ class HaLabeledSlider extends LitElement { > ${this.helper - ? html` ${this.helper} ` + ? html` + ${this.helper} + ` : nothing} `; } diff --git a/src/components/ha-selector/ha-selector-datetime.ts b/src/components/ha-selector/ha-selector-datetime.ts index 1c5e2bf8db..3f0c4f544d 100644 --- a/src/components/ha-selector/ha-selector-datetime.ts +++ b/src/components/ha-selector/ha-selector-datetime.ts @@ -54,7 +54,9 @@ export class HaDateTimeSelector extends LitElement { > ${this.helper - ? html`${this.helper}` + ? html`${this.helper}` : ""} `; } diff --git a/src/components/ha-selector/ha-selector-number.ts b/src/components/ha-selector/ha-selector-number.ts index 3249f96aad..66fed152d9 100644 --- a/src/components/ha-selector/ha-selector-number.ts +++ b/src/components/ha-selector/ha-selector-number.ts @@ -106,7 +106,9 @@ export class HaNumberSelector extends LitElement { ${!isBox && this.helper - ? html`${this.helper}` + ? html`${this.helper}` : nothing} `; } diff --git a/src/components/ha-selector/ha-selector-object.ts b/src/components/ha-selector/ha-selector-object.ts index e328112f35..f2b9040b20 100644 --- a/src/components/ha-selector/ha-selector-object.ts +++ b/src/components/ha-selector/ha-selector-object.ts @@ -38,7 +38,9 @@ export class HaObjectSelector extends LitElement { @value-changed=${this._handleChange} > ${this.helper - ? html`${this.helper}` + ? html`${this.helper}` : ""} `; } diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index c136290114..f6b31bc33d 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -285,7 +285,9 @@ export class HaSelectSelector extends LitElement { private _renderHelper() { return this.helper - ? html`${this.helper}` + ? html`${this.helper}` : ""; } diff --git a/src/components/ha-selector/ha-selector-template.ts b/src/components/ha-selector/ha-selector-template.ts index 50f6e039ed..555f09bd17 100644 --- a/src/components/ha-selector/ha-selector-template.ts +++ b/src/components/ha-selector/ha-selector-template.ts @@ -63,7 +63,9 @@ export class HaTemplateSelector extends LitElement { linewrap > ${this.helper - ? html`${this.helper}` + ? html`${this.helper}` : nothing} `; } diff --git a/src/components/ha-target-picker.ts b/src/components/ha-target-picker.ts index 01624644ae..0f5a4ae05c 100644 --- a/src/components/ha-target-picker.ts +++ b/src/components/ha-target-picker.ts @@ -289,7 +289,9 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) { ${this._renderPicker()} ${this.helper - ? html`${this.helper}` + ? html`${this.helper}` : ""} `; } From 573c1db081a16e111ce191a43b33b61d67f4b3f5 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Thu, 19 Jun 2025 12:49:07 +0200 Subject: [PATCH 058/216] Add hyphen to all compounds with "-powered" (#25840) --- src/translations/en.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index b352f0a58e..fb5ec5df32 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1753,7 +1753,7 @@ "heading": "Reconfiguring device", "configuring_alt": "Configuring", "introduction": "Reconfigure a device on your Zigbee network. Use this feature if your device is not functioning correctly.", - "battery_device_warning": "You will need to wake battery powered devices before starting the reconfiguration process. Refer to your device's manual for instructions on how to wake the device.", + "battery_device_warning": "You will need to wake battery-powered devices before starting the reconfiguration process. Refer to your device's manual for instructions on how to wake the device.", "run_in_background": "You can close this dialog and the reconfiguration will continue in the background.", "start_reconfiguration": "Start reconfiguration", "in_progress": "The device is being reconfigured. This may take some time.", @@ -1789,7 +1789,7 @@ "view_network": "View network" }, "services": { - "reconfigure": "Reconfigure ZHA device (heal device). Use this if you are having issues with the device. If the device in question is a battery powered device please ensure it is awake and accepting commands when you use this action.", + "reconfigure": "Reconfigure ZHA device (heal device). Use this if you are having issues with the device. If the device in question is a battery-powered device please ensure it is awake and accepting commands when you use this action.", "updateDeviceName": "Set a custom name for this device in the device registry.", "remove": "Remove a device from the Zigbee network.", "zigbee_information": "View the Zigbee information for the device." @@ -3429,7 +3429,7 @@ }, "speech": { "title": "Amazing speech options for Assist", - "text": "Bring personality to your home by having it speak to you using our neural-network powered speech-to-text and text-to-speech services." + "text": "Bring personality to your home by having it speak to you using our neural-network-powered speech-to-text and text-to-speech services." }, "remote_access": { "title": "Remote access", @@ -6165,7 +6165,7 @@ "reinterview_node": { "title": "Re-interview a Z-Wave device", "introduction": "Re-interview a device on your Z-Wave network. Use this feature if your device has missing or incorrect functionality.", - "battery_device_warning": "You will need to wake battery powered devices before starting the re-interview. Refer to your device's manual for instructions on how to wake the device.", + "battery_device_warning": "You will need to wake battery-powered devices before starting the re-interview. Refer to your device's manual for instructions on how to wake the device.", "run_in_background": "You can close this dialog and the interview will continue in the background.", "start_reinterview": "Start re-interview", "in_progress": "The device is being interviewed. This may take some time.", @@ -6367,7 +6367,7 @@ "reinterview_node": { "title": "Re-interview a Matter device", "introduction": "Perform a full re-interview of a Matter device. Use this feature only if your device has missing or incorrect functionality.", - "battery_device_warning": "You will need to wake battery powered devices before starting the re-interview. Refer to your device's manual for instructions on how to wake the device.", + "battery_device_warning": "You will need to wake battery-powered devices before starting the re-interview. Refer to your device's manual for instructions on how to wake the device.", "run_in_background": "You can close this dialog and the interview will continue in the background.", "start_reinterview": "Start re-interview", "in_progress": "The device is being interviewed. This may take some time.", @@ -6377,7 +6377,7 @@ "ping_node": { "title": "Ping a Matter device", "introduction": "Perform a (server-side) ping on your Matter device on all its (known) IP-addresses.", - "battery_device_warning": "Note that especially for battery powered devices this can take a while. You may need to wake up battery powered devices before starting the pinging to speed up the process. Refer to your device's manual for instructions on how to wake the device.", + "battery_device_warning": "Note that especially for battery-powered devices this can take a while. You may need to wake up battery-powered devices before starting the pinging to speed up the process. Refer to your device's manual for instructions on how to wake the device.", "start_ping": "Start ping", "in_progress": "The device is being pinged. This may take some time.", "ping_failed": "The device ping failed. Additional information may be available in the logs.", From 65e78de41c720d78a8d33acd10599ed5cc9cba20 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Thu, 19 Jun 2025 13:00:22 +0200 Subject: [PATCH 059/216] Improve explanation of 'Ping a Matter device' dialog (#25839) * Fix explanation of 'Ping a Matter device' dialog - change to more descriptive wording using third-person singular - replace first "on" with "of" to clarify (currently "on" contradicts "server-side") - remove the wrong hyphen in "IP-addresses" --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index fb5ec5df32..49a67da5f6 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6376,7 +6376,7 @@ }, "ping_node": { "title": "Ping a Matter device", - "introduction": "Perform a (server-side) ping on your Matter device on all its (known) IP-addresses.", + "introduction": "This performs a (server-side) ping of your Matter device on all its (known) IP addresses.", "battery_device_warning": "Note that especially for battery-powered devices this can take a while. You may need to wake up battery-powered devices before starting the pinging to speed up the process. Refer to your device's manual for instructions on how to wake the device.", "start_ping": "Start ping", "in_progress": "The device is being pinged. This may take some time.", From 6aa8a24aad23da47fc5410947b83c66d6f85b31b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 19 Jun 2025 14:51:11 +0200 Subject: [PATCH 060/216] Fix for my links ending with a slash (#25841) --- src/panels/my/ha-panel-my.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/panels/my/ha-panel-my.ts b/src/panels/my/ha-panel-my.ts index 39e97a566b..c1e091aaa1 100644 --- a/src/panels/my/ha-panel-my.ts +++ b/src/panels/my/ha-panel-my.ts @@ -349,7 +349,10 @@ class HaPanelMy extends LitElement { connectedCallback() { super.connectedCallback(); - const path = this.route.path.substring(1); + const path = this.route.path.substring( + 1, + this.route.path.endsWith("/") ? this.route.path.length - 1 : undefined + ); const hasSupervisor = isComponentLoaded(this.hass, "hassio"); this._redirect = getRedirect(path); From 8ee80586a8719a8729fc2ba3c7c538f76e052e8d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 16:12:02 +0200 Subject: [PATCH 061/216] Update vaadinWebComponents monorepo to v24.8.0 (#25842) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 178 +++++++++++++++++++++++++++------------------------ 2 files changed, 95 insertions(+), 87 deletions(-) diff --git a/package.json b/package.json index b1a5586c5d..73d3cb3724 100644 --- a/package.json +++ b/package.json @@ -89,8 +89,8 @@ "@thomasloven/round-slider": "0.6.0", "@tsparticles/engine": "3.8.1", "@tsparticles/preset-links": "3.2.0", - "@vaadin/combo-box": "24.7.8", - "@vaadin/vaadin-themable-mixin": "24.7.8", + "@vaadin/combo-box": "24.8.0", + "@vaadin/vaadin-themable-mixin": "24.8.0", "@vibrant/color": "4.0.0", "@vue/web-component-wrapper": "1.3.0", "@webcomponents/scoped-custom-element-registry": "0.0.10", diff --git a/yarn.lock b/yarn.lock index 5b3f9ca7ce..173c0c129b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5008,131 +5008,131 @@ __metadata: languageName: node linkType: hard -"@vaadin/a11y-base@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/a11y-base@npm:24.7.8" +"@vaadin/a11y-base@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/a11y-base@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.7.8" + "@vaadin/component-base": "npm:^24.8.0" lit: "npm:^3.0.0" - checksum: 10/03e8b0d954b65ee96077a0a40c4300bcadb4c59a47bba8f68505bb319b0fb6b06aa1507a9228a92d7c95e26633611901b98d85bf9abcf5cf17a06e9af9bec785 + checksum: 10/67aa4700b9f385aa92ca18e68abaa2fb592336673f2286dd2ef2b5eaba1fb8b882f454590fb68e975d4caf0a8698a953ffddcece943d10f91b960272152db564 languageName: node linkType: hard -"@vaadin/combo-box@npm:24.7.8": - version: 24.7.8 - resolution: "@vaadin/combo-box@npm:24.7.8" +"@vaadin/combo-box@npm:24.8.0": + version: 24.8.0 + resolution: "@vaadin/combo-box@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.7.8" - "@vaadin/component-base": "npm:~24.7.8" - "@vaadin/field-base": "npm:~24.7.8" - "@vaadin/input-container": "npm:~24.7.8" - "@vaadin/item": "npm:~24.7.8" - "@vaadin/lit-renderer": "npm:~24.7.8" - "@vaadin/overlay": "npm:~24.7.8" - "@vaadin/vaadin-lumo-styles": "npm:~24.7.8" - "@vaadin/vaadin-material-styles": "npm:~24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.8" + "@vaadin/a11y-base": "npm:^24.8.0" + "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/field-base": "npm:^24.8.0" + "@vaadin/input-container": "npm:^24.8.0" + "@vaadin/item": "npm:^24.8.0" + "@vaadin/lit-renderer": "npm:^24.8.0" + "@vaadin/overlay": "npm:^24.8.0" + "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" + "@vaadin/vaadin-material-styles": "npm:^24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" lit: "npm:^3.0.0" - checksum: 10/42cf36993f6041556d08f5e4d50a33fba07aff788ee23a388ee7ebf0672184560b32993492dddb6a89e0ee99ee458bbc5ed8bfd53a39b9280bd2cad1182c11c4 + checksum: 10/c448e127ec53abfd3beca525518342a6d61ad62874e5c8ed3dea56cab66b7f9e132d40d6727ebe42dad53d0b343d30405ca6d9018d3a3a8b7b96c1f50b6d0133 languageName: node linkType: hard -"@vaadin/component-base@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/component-base@npm:24.7.8" +"@vaadin/component-base@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/component-base@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" "@vaadin/vaadin-development-mode-detector": "npm:^2.0.0" "@vaadin/vaadin-usage-statistics": "npm:^2.1.0" lit: "npm:^3.0.0" - checksum: 10/a353b972d315553456f828ce5ddef6c4139fe793f460bf1eea70dd8e0343a7adad7a626fbcbf6284681e0ff04c6b3ae6fe2febd78ea6121edf58751036ba5552 + checksum: 10/7111877c340af80fceb9042d3a58f7916579b0c6639268fa899bc107f61c357b65154e07247232ca9f922ba3399c43b1176fa2226f4faa1091f51ab11c74c572 languageName: node linkType: hard -"@vaadin/field-base@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/field-base@npm:24.7.8" +"@vaadin/field-base@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/field-base@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.7.8" - "@vaadin/component-base": "npm:~24.7.8" + "@vaadin/a11y-base": "npm:^24.8.0" + "@vaadin/component-base": "npm:^24.8.0" lit: "npm:^3.0.0" - checksum: 10/9f7fa9eada7e46f5150052d0d95f3c649a4240d9bf200c1a2b1a7820cf6f9bb0aae2f5f9ba15570e43af783178aab4000ea7259cf0c64c240eb18d325a056826 + checksum: 10/3357d43a310464d9c76f10e782ec658ae5af9b5a9308faf6de878e3e110b4c20ebc6af8b7f33954f73cfd0f527798787c1994498675067cc814c01ef0588deeb languageName: node linkType: hard -"@vaadin/icon@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/icon@npm:24.7.8" +"@vaadin/icon@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/icon@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.7.8" - "@vaadin/vaadin-lumo-styles": "npm:~24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.8" + "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" lit: "npm:^3.0.0" - checksum: 10/2936379fbb24ab318c65429fabd2291bd8fc7f9cbb99505b40444bfc50d68059935ab9b8f176182fdaba228e1cea97c477644bd91b72e47c01cc21eb613118a9 + checksum: 10/e0a90e694179d59228c32e8e93c89e0cc2ebc9509995d6a24679137086796a116b6de9c7fd2e64cec532445bbb20a9991f958d6482cedbf271d041e611163558 languageName: node linkType: hard -"@vaadin/input-container@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/input-container@npm:24.7.8" +"@vaadin/input-container@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/input-container@npm:24.8.0" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.7.8" - "@vaadin/vaadin-lumo-styles": "npm:~24.7.8" - "@vaadin/vaadin-material-styles": "npm:~24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.8" + "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" + "@vaadin/vaadin-material-styles": "npm:^24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" lit: "npm:^3.0.0" - checksum: 10/892a2bf1820d09df5e9fcd99dc4985f4fc4cc57ee34de92051c1eb732769f1101bdf90bcc33e34f0b78d68e3bdcbc99514e75470945a6dd810d1662d8035149a + checksum: 10/b9ae6590936f68cfb77d5d4ff29b7acb63c16482059391efd64f72837984df084aeb6f39313adb3ea482496adb6f3d84a3195dd265d59f508ccffcb0d5d7b9a4 languageName: node linkType: hard -"@vaadin/item@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/item@npm:24.7.8" +"@vaadin/item@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/item@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.7.8" - "@vaadin/component-base": "npm:~24.7.8" - "@vaadin/vaadin-lumo-styles": "npm:~24.7.8" - "@vaadin/vaadin-material-styles": "npm:~24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.8" + "@vaadin/a11y-base": "npm:^24.8.0" + "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" + "@vaadin/vaadin-material-styles": "npm:^24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" lit: "npm:^3.0.0" - checksum: 10/b0fdefdf3855d0bcc5faf97ae0a3cce07856fb31041ee95c05df32cc30e40b8f08266d4751fd72187f4869abb3646ed97a8474d7a0ea4c4d4a02e0f0f5ee8cc3 + checksum: 10/a4eb618e7d204147379732244fc5bf3cdf7420ce168f91e0db9cb2a809fad267af4f54eac117a7fbb287a33489a12c7d4724d3a4e7a7fdd415ac30eb6f3554b5 languageName: node linkType: hard -"@vaadin/lit-renderer@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/lit-renderer@npm:24.7.8" +"@vaadin/lit-renderer@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/lit-renderer@npm:24.8.0" dependencies: lit: "npm:^3.0.0" - checksum: 10/2e82de7fda3eb8a01357697ff55cb46905fe30382261cab0ea3f685e7b51aa4c78b90ea6444797bf773806c71b00c198a3087e9610f679f5b2861c4100b138ac + checksum: 10/f843d389f8f1958ec70d5180535a620e774f90bd3e3e5e77e41c6b89f4820272df2a68a70a5d6d752dbb3078b966985767d8ca079054a5fc0daa95d000524d27 languageName: node linkType: hard -"@vaadin/overlay@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/overlay@npm:24.7.8" +"@vaadin/overlay@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/overlay@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.7.8" - "@vaadin/component-base": "npm:~24.7.8" - "@vaadin/vaadin-lumo-styles": "npm:~24.7.8" - "@vaadin/vaadin-material-styles": "npm:~24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.8" + "@vaadin/a11y-base": "npm:^24.8.0" + "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" + "@vaadin/vaadin-material-styles": "npm:^24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" lit: "npm:^3.0.0" - checksum: 10/d111bb3bd26fcd5f92576425eaad12d94f2dd386ec710d71be86373dbd2f5678ac5db5bb7bdaaf244acb7342dc83e89572a733c1541ce44961c0e60c46d4a2b3 + checksum: 10/d26330e761868a682013937c66977b5c6662251afa32df7fed0aea218977c25dc0f24a8d9b96561754b746943563c82c7a91e365cf50f8713a549562d63d9ec7 languageName: node linkType: hard @@ -5143,36 +5143,37 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-lumo-styles@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/vaadin-lumo-styles@npm:24.7.8" +"@vaadin/vaadin-lumo-styles@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/vaadin-lumo-styles@npm:24.8.0" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.7.8" - "@vaadin/icon": "npm:~24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.8" - checksum: 10/f1d8fcf2d99a2e504e48df334e3c66509936f8a67df9cd5d552273bb854277ad898ec578a883e2bdd9417957b42837436be90571ed3d4f0af899a76f65658ff4 + "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/icon": "npm:^24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" + checksum: 10/14d9c942ed88a9aa4579f0c8b8193d51affe3c0fdff781ecb9fb53aef6746d86317eb372526a15b0723017cd67b16bbb3ed252b01c89faa89bb0fca3d1b19855 languageName: node linkType: hard -"@vaadin/vaadin-material-styles@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/vaadin-material-styles@npm:24.7.8" +"@vaadin/vaadin-material-styles@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/vaadin-material-styles@npm:24.8.0" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.8" - checksum: 10/c62968c62ea27aa33e349f5c36d222eb2afb8193eeb11eb4f1609e68755220888a3571e0c503b3b6649ee16a1ea04c0f225ad44572cc076c381d10e08a004b98 + "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" + checksum: 10/87917d5b20a1d77e7e38aff1e5be1d28a52a7c08777a01f44a6198cb4f0904ddf7257b23d622a02d098f38928ae3b168e2f2c81ac2c7b952e8056b4958154692 languageName: node linkType: hard -"@vaadin/vaadin-themable-mixin@npm:24.7.8, @vaadin/vaadin-themable-mixin@npm:~24.7.8": - version: 24.7.8 - resolution: "@vaadin/vaadin-themable-mixin@npm:24.7.8" +"@vaadin/vaadin-themable-mixin@npm:24.8.0, @vaadin/vaadin-themable-mixin@npm:^24.8.0": + version: 24.8.0 + resolution: "@vaadin/vaadin-themable-mixin@npm:24.8.0" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" lit: "npm:^3.0.0" - checksum: 10/72522cf9001ecf4c0cfd4cb8a2d5dc0f5781962118d0376df3b23bbfc64f731b411fda26efd6cd8edcd997634e45396e3097ed61752ec2b184c09795342de825 + style-observer: "npm:^0.0.8" + checksum: 10/141f8756e3330b9ee4efdd34e2febf832a70cf86802908d7fec589567bd4d3ce2820345cbe39fe5a6315bc6dee84065f781f48842ce864ef0a59589607ba1faa languageName: node linkType: hard @@ -9322,8 +9323,8 @@ __metadata: "@types/tar": "npm:6.1.13" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@vaadin/combo-box": "npm:24.7.8" - "@vaadin/vaadin-themable-mixin": "npm:24.7.8" + "@vaadin/combo-box": "npm:24.8.0" + "@vaadin/vaadin-themable-mixin": "npm:24.8.0" "@vibrant/color": "npm:4.0.0" "@vitest/coverage-v8": "npm:3.2.3" "@vue/web-component-wrapper": "npm:1.3.0" @@ -13850,6 +13851,13 @@ __metadata: languageName: node linkType: hard +"style-observer@npm:^0.0.8": + version: 0.0.8 + resolution: "style-observer@npm:0.0.8" + checksum: 10/9c72ee12c61d48f64622a625ebff9bc4df009877e7ed9b26cec08e8159f6270f428aeea120f0e7c5567c8bbaa701846528fb5339dbdb930e84f2a66d382aeeb6 + languageName: node + linkType: hard + "superstruct@npm:2.0.2": version: 2.0.2 resolution: "superstruct@npm:2.0.2" From f7634c45c23b28d1ad84de927acd8ca90cee339b Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Thu, 19 Jun 2025 18:49:58 +0300 Subject: [PATCH 062/216] Fix bar chart data order when using the legend (#25832) * Fix bar chart data order when using the legend * type fix --- src/components/chart/ha-chart-base.ts | 77 ++++++++++--------- .../hui-energy-devices-detail-graph-card.ts | 11 ++- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index b40256b60b..fdaad85c83 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -9,6 +9,7 @@ import type { LegendComponentOption, XAXisOption, YAXisOption, + LineSeriesOption, } from "echarts/types/dist/shared"; import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; @@ -642,44 +643,46 @@ export class HaChartBase extends LitElement { const yAxis = (this.options?.yAxis?.[0] ?? this.options?.yAxis) as | YAXisOption | undefined; - const series = ensureArray(this.data) - .filter((d) => !this._hiddenDatasets.has(String(d.name ?? d.id))) - .map((s) => { - if (s.type === "line") { - if (yAxis?.type === "log") { - // set <=0 values to null so they render as gaps on a log graph - return { - ...s, - data: s.data?.map((v) => - Array.isArray(v) - ? [ - v[0], - typeof v[1] !== "number" || v[1] > 0 ? v[1] : null, - ...v.slice(2), - ] - : v - ), - }; - } - if (s.sampling === "minmax") { - const minX = - xAxis?.min && typeof xAxis.min === "number" - ? xAxis.min - : undefined; - const maxX = - xAxis?.max && typeof xAxis.max === "number" - ? xAxis.max - : undefined; - return { - ...s, - sampling: undefined, - data: downSampleLineData(s.data, this.clientWidth, minX, maxX), - }; - } + const series = ensureArray(this.data).map((s) => { + const data = this._hiddenDatasets.has(String(s.name ?? s.id)) + ? undefined + : s.data; + if (data && s.type === "line") { + if (yAxis?.type === "log") { + // set <=0 values to null so they render as gaps on a log graph + return { + ...s, + data: (data as LineSeriesOption["data"])!.map((v) => + Array.isArray(v) + ? [ + v[0], + typeof v[1] !== "number" || v[1] > 0 ? v[1] : null, + ...v.slice(2), + ] + : v + ), + }; } - return s; - }); - return series; + if (s.sampling === "minmax") { + const minX = + xAxis?.min && typeof xAxis.min === "number" ? xAxis.min : undefined; + const maxX = + xAxis?.max && typeof xAxis.max === "number" ? xAxis.max : undefined; + return { + ...s, + sampling: undefined, + data: downSampleLineData( + data as LineSeriesOption["data"], + this.clientWidth, + minX, + maxX + ), + }; + } + } + return { ...s, data }; + }); + return series as ECOption["series"]; } private _getDefaultHeight() { diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts index 917d9e5950..c4ef01c62a 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts @@ -81,7 +81,6 @@ export class HuiEnergyDevicesDetailGraphCard key: this._config?.collection_key, }).subscribe((data) => { this._data = data; - this._processStatistics(); }), ]; } @@ -103,10 +102,7 @@ export class HuiEnergyDevicesDetailGraphCard } protected willUpdate(changedProps: PropertyValues) { - if ( - (changedProps.has("_hiddenStats") || changedProps.has("_config")) && - this._data - ) { + if (changedProps.has("_config") || changedProps.has("_data")) { this._processStatistics(); } } @@ -206,7 +202,10 @@ export class HuiEnergyDevicesDetailGraphCard ); private _processStatistics() { - const energyData = this._data!; + if (!this._data) { + return; + } + const energyData = this._data; this._start = energyData.start; this._end = energyData.end || endOfToday(); From b20d489bdda90d8f96396a2c71ee328f830aa700 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Thu, 19 Jun 2025 21:29:07 +0200 Subject: [PATCH 063/216] Fix sentence-casing of Z-Wave strings (#25846) * Fix sentence-casing of Z-Wave strings * Also fix casing of "Z-Wave JS Device Database" --- src/translations/en.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 49a67da5f6..d64c5f2ce5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5983,7 +5983,7 @@ "introduction": "Manage and adjust device specific configuration parameters for the selected device", "attribution": "Device configuration parameters and descriptions are provided by the {device_database}", "endpoint": "Endpoint {endpoint}", - "zwave_js_device_database": "Z-Wave JS Device Database", + "zwave_js_device_database": "Z-Wave JS device database", "battery_device_notice": "Battery devices must be awake to update their config. Please refer to your device manual for instructions on how to wake the device.", "parameter_is_read_only": "This parameter is read-only.", "between_min_max": "Between {min} and {max}", @@ -6240,21 +6240,21 @@ } }, "logs": { - "title": "Z-Wave JS Logs", + "title": "Z-Wave JS logs", "log_level": "Log level", "subscribed_to_logs": "Subscribed to Z-Wave JS log messages…", - "log_level_changed": "Log Level changed to: {level}", + "log_level_changed": "Log level changed to: {level}", "download_logs": "Download logs" }, "node_installer": { - "header": "Installer Settings", + "header": "Installer settings", "introduction": "Configure your device installer settings.", "endpoint": "Endpoint", "no_settings": "This device does not have any installer settings.", - "command_class": "Command Class", + "command_class": "Command class", "capability_controls": { "thermostat_setback": { - "title": "Thermostat Setback", + "title": "Thermostat setback", "setback_state_label": "Setback in Kelvin", "setback_state_helper": "Min: -12.8, max: 12.0", "setback_special_state": { @@ -6264,7 +6264,7 @@ "unused": "Unused" }, "setback_type": { - "label": "Setback Type", + "label": "Setback type", "none": "None", "temporary": "Temporary", "permanent": "Permanent" @@ -6284,7 +6284,7 @@ "control_failed": "Failed to control transition. {error}" }, "door_lock": { - "title": "Door Lock", + "title": "Door lock", "twist_assist": "Twist assist", "block_to_block": "Block to block", "auto_relock_time": "Auto relock time", @@ -6311,8 +6311,8 @@ "color_switch": { "color_component": "Color component", "colors": { - "0": "Warm White", - "1": "Cold White", + "0": "Warm white", + "1": "Cold white", "2": "Red", "3": "Green", "4": "Blue", From e9272b9a27c6fa56084d81770958658d044af5ea Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 20 Jun 2025 04:50:12 -0700 Subject: [PATCH 064/216] Remove gray colors from chart color set (#25844) --- src/common/color/colors.ts | 4 ---- .../cards/energy/hui-energy-devices-detail-graph-card.ts | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/common/color/colors.ts b/src/common/color/colors.ts index aace900bbd..72aba65602 100644 --- a/src/common/color/colors.ts +++ b/src/common/color/colors.ts @@ -11,7 +11,6 @@ export const COLORS = [ "#9c6b4e", "#97bbf5", "#01ab63", - "#9498a0", "#094bad", "#c99000", "#d84f3e", @@ -21,7 +20,6 @@ export const COLORS = [ "#8043ce", "#7599d1", "#7a4c31", - "#74787f", "#6989f4", "#ffd444", "#ff957c", @@ -31,7 +29,6 @@ export const COLORS = [ "#c884ff", "#badeff", "#bf8b6d", - "#b6bac2", "#927acc", "#97ee3f", "#bf3947", @@ -44,7 +41,6 @@ export const COLORS = [ "#d9b100", "#9d7a00", "#698cff", - "#d9d9d9", "#00d27e", "#d06800", "#009f82", diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts index c4ef01c62a..d3d4839431 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts @@ -392,7 +392,7 @@ export class HuiEnergyDevicesDetailGraphCard this.hass.themes.darkMode, false, compare, - "--state-unavailable-color" + "--history-unknown-color" ), }, barMaxWidth: 50, @@ -401,7 +401,7 @@ export class HuiEnergyDevicesDetailGraphCard this.hass.themes.darkMode, true, compare, - "--state-unavailable-color" + "--history-unknown-color" ), data: untrackedConsumption, stack: compare ? "devicesCompare" : "devices", From f47336392cef0bcfddebea55420e1d0579642b69 Mon Sep 17 00:00:00 2001 From: Bastian Date: Fri, 20 Jun 2025 14:01:02 +0200 Subject: [PATCH 065/216] Fix/dhcp config network sort (#25799) * Add ip sort method to compare helper * Add ip sort functionality to dhcp config panel datatable * Add type ip to DataTableColumnData * Change ip sorting to padStart method for better readablity * Rename ip compare method to clarify ipv4 * Enhance IP compare method to include ipv6 * Add compare IP test --- src/common/string/compare.ts | 53 +++++++++++++++++++ src/components/data-table/ha-data-table.ts | 1 + .../data-table/sort-filter-worker.ts | 4 +- .../dhcp/dhcp-config-panel.ts | 1 + test/common/string/compare_ip.test.ts | 34 ++++++++++++ 5 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 test/common/string/compare_ip.test.ts diff --git a/src/common/string/compare.ts b/src/common/string/compare.ts index 65952c2922..9d6aca76b8 100644 --- a/src/common/string/compare.ts +++ b/src/common/string/compare.ts @@ -1,4 +1,5 @@ import memoizeOne from "memoize-one"; +import { isIPAddress } from "./is_ip_address"; const collator = memoizeOne( (language: string | undefined) => new Intl.Collator(language) @@ -33,6 +34,19 @@ export const stringCompare = ( return fallbackStringCompare(a, b); }; +export const ipCompare = (a: string, b: string) => { + const aIsIpV4 = isIPAddress(a); + const bIsIpV4 = isIPAddress(b); + + if (aIsIpV4 && bIsIpV4) { + return ipv4Compare(a, b); + } + if (!aIsIpV4 && !bIsIpV4) { + return ipV6Compare(a, b); + } + return aIsIpV4 ? -1 : 1; +}; + export const caseInsensitiveStringCompare = ( a: string, b: string, @@ -64,3 +78,42 @@ export const orderCompare = (order: string[]) => (a: string, b: string) => { return idxA - idxB; }; + +function ipv4Compare(a: string, b: string) { + const num1 = Number( + a + .split(".") + .map((num) => num.padStart(3, "0")) + .join("") + ); + const num2 = Number( + b + .split(".") + .map((num) => num.padStart(3, "0")) + .join("") + ); + return num1 - num2; +} + +function ipV6Compare(a: string, b: string) { + const ipv6a = normalizeIPv6(a) + .split(":") + .map((part) => part.padStart(4, "0")) + .join(""); + const ipv6b = normalizeIPv6(b) + .split(":") + .map((part) => part.padStart(4, "0")) + .join(""); + + return ipv6a.localeCompare(ipv6b); +} + +function normalizeIPv6(ip) { + const parts = ip.split("::"); + const head = parts[0].split(":"); + const tail = parts[1] ? parts[1].split(":") : []; + const totalParts = 8; + const missing = totalParts - (head.length + tail.length); + const zeros = new Array(missing).fill("0"); + return [...head, ...zeros, ...tail].join(":"); +} diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index b0cf4bab88..0369758b29 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -72,6 +72,7 @@ export interface DataTableColumnData extends DataTableSortColumnData { label?: TemplateResult | string; type?: | "numeric" + | "ip" | "icon" | "icon-button" | "overflow" diff --git a/src/components/data-table/sort-filter-worker.ts b/src/components/data-table/sort-filter-worker.ts index b34ec899ff..a4da225cbd 100644 --- a/src/components/data-table/sort-filter-worker.ts +++ b/src/components/data-table/sort-filter-worker.ts @@ -1,5 +1,5 @@ import { expose } from "comlink"; -import { stringCompare } from "../../common/string/compare"; +import { stringCompare, ipCompare } from "../../common/string/compare"; import { stripDiacritics } from "../../common/string/strip-diacritics"; import type { ClonedDataTableColumnData, @@ -57,6 +57,8 @@ const sortData = ( if (column.type === "numeric") { valA = isNaN(valA) ? undefined : Number(valA); valB = isNaN(valB) ? undefined : Number(valB); + } else if (column.type === "ip") { + return sort * ipCompare(valA, valB); } else if (typeof valA === "string" && typeof valB === "string") { return sort * stringCompare(valA, valB, language); } diff --git a/src/panels/config/integrations/integration-panels/dhcp/dhcp-config-panel.ts b/src/panels/config/integrations/integration-panels/dhcp/dhcp-config-panel.ts index 683719ee43..fdf8768c6f 100644 --- a/src/panels/config/integrations/integration-panels/dhcp/dhcp-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/dhcp/dhcp-config-panel.ts @@ -60,6 +60,7 @@ export class DHCPConfigPanel extends SubscribeMixin(LitElement) { title: localize("ui.panel.config.dhcp.ip_address"), filterable: true, sortable: true, + type: "ip", }, }; diff --git a/test/common/string/compare_ip.test.ts b/test/common/string/compare_ip.test.ts new file mode 100644 index 0000000000..ddb7e7146a --- /dev/null +++ b/test/common/string/compare_ip.test.ts @@ -0,0 +1,34 @@ +import { assert, describe, it } from "vitest"; +import { ipCompare } from "../../../src/common/string/compare"; +import { isIPAddress } from "../../../src/common/string/is_ip_address"; + +describe("compareIpAdresses", () => { + const ipAddresses: string[] = [ + "192.168.1.1", + "10.0.0.1", + "fe80::85d:e82c:9446:7995", + "192.168.0.1", + "fe80::85d:e82c:9446:7994", + "::ffff:192.168.1.1", + "1050:0000:0000:0000:0005:0600:300c:326b", + ]; + const expected: string[] = [ + "10.0.0.1", + "192.168.0.1", + "192.168.1.1", + "::ffff:192.168.1.1", + "1050:0000:0000:0000:0005:0600:300c:326b", + "fe80::85d:e82c:9446:7994", + "fe80::85d:e82c:9446:7995", + ]; + + const sorted = [...ipAddresses].sort(ipCompare); + + it("Detects ipv4 addresses", () => { + assert.isTrue(isIPAddress("192.168.0.1")); + }); + + it("Compares ipv4 and ipv6 addresses", () => { + assert.deepEqual(sorted, expected); + }); +}); From d58186fec9929a352244e5764c562544ceceb1ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 15:01:44 +0300 Subject: [PATCH 066/216] Update dependency typescript-eslint to v8.34.1 (#25848) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 146 +++++++++++++++++++++++++-------------------------- 2 files changed, 74 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index 73d3cb3724..5af921e32f 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,7 @@ "terser-webpack-plugin": "5.3.14", "ts-lit-plugin": "2.0.2", "typescript": "5.8.3", - "typescript-eslint": "8.34.0", + "typescript-eslint": "8.34.1", "vite-tsconfig-paths": "5.1.4", "vitest": "3.2.3", "webpack-stats-plugin": "1.1.3", diff --git a/yarn.lock b/yarn.lock index 173c0c129b..d975b035fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4872,105 +4872,105 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.34.0" +"@typescript-eslint/eslint-plugin@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.34.1" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.34.0" - "@typescript-eslint/type-utils": "npm:8.34.0" - "@typescript-eslint/utils": "npm:8.34.0" - "@typescript-eslint/visitor-keys": "npm:8.34.0" + "@typescript-eslint/scope-manager": "npm:8.34.1" + "@typescript-eslint/type-utils": "npm:8.34.1" + "@typescript-eslint/utils": "npm:8.34.1" + "@typescript-eslint/visitor-keys": "npm:8.34.1" graphemer: "npm:^1.4.0" ignore: "npm:^7.0.0" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.1.0" peerDependencies: - "@typescript-eslint/parser": ^8.34.0 + "@typescript-eslint/parser": ^8.34.1 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/224f9e8a596e3c37fade2f2a1a9efce2fad652a768710693458e2b7c7f88c3a0e7bbbbc46d34d839c9373861fac542de6b9a7e132e36e2819b63840b9529e605 + checksum: 10/a4b1cffcb5f2b4f5f4c267cd4519d0e2df73c8017c93200d5a86df7882073f18cf4f5d0604aa8dafb6e4dc4ab391ae8e9a2161631fb1eca9bca32af063acdaf2 languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/parser@npm:8.34.0" +"@typescript-eslint/parser@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/parser@npm:8.34.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.34.0" - "@typescript-eslint/types": "npm:8.34.0" - "@typescript-eslint/typescript-estree": "npm:8.34.0" - "@typescript-eslint/visitor-keys": "npm:8.34.0" + "@typescript-eslint/scope-manager": "npm:8.34.1" + "@typescript-eslint/types": "npm:8.34.1" + "@typescript-eslint/typescript-estree": "npm:8.34.1" + "@typescript-eslint/visitor-keys": "npm:8.34.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/b4c03ff2f09fd800a8f28c24289d24e2f4bfb4745c122f5f496bf832b06f0f37b1ab31ce8d7590ff1f83253de3306d145ef7b3c7b853a4ae716cb7ff443d1c27 + checksum: 10/c862baa6f5260bf4b63d79ae4d68fc09b7e094ea9f28ee461887cbb660ef1339e829119029e1e6ba40335fc9e85d134a04036965bc261f7abf4d0e605cb485ec languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/project-service@npm:8.34.0" +"@typescript-eslint/project-service@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/project-service@npm:8.34.1" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.34.0" - "@typescript-eslint/types": "npm:^8.34.0" + "@typescript-eslint/tsconfig-utils": "npm:^8.34.1" + "@typescript-eslint/types": "npm:^8.34.1" debug: "npm:^4.3.4" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/04763896215c208c6b29e0b4f66ee0621878cd88fb6d9008c543db57f1d6b5d7fcc88f048c9a66ba2ed797f68e563c350e1b65403349ef75a4bc419072cef3c8 + checksum: 10/5dad268397cd2d601e5e65ab9628c59c6687a79cac31e0d0eac2b434505639fd8767f1a2d5b077b158c581f5a48bb96e7da361560fb26b70b680272e39c6f976 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/scope-manager@npm:8.34.0" +"@typescript-eslint/scope-manager@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/scope-manager@npm:8.34.1" dependencies: - "@typescript-eslint/types": "npm:8.34.0" - "@typescript-eslint/visitor-keys": "npm:8.34.0" - checksum: 10/fec7bb94fb3848bdf5ab9cfaf634e56aec3ed9bc4d546f65d83bb6511452e5a4b9eed5d09f54efceb9fa3b23a451d409735359237e8c0d51233d6537e5449fa7 + "@typescript-eslint/types": "npm:8.34.1" + "@typescript-eslint/visitor-keys": "npm:8.34.1" + checksum: 10/cd3f2ba811e4794c78d7f9df0ff1ad6ce33d162d87986e67c4ec409963f07384bd184dbddc613e89a5cc753a47469e7fa9d02c507b57aa9e0fdc8a97c0378353 languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.34.0, @typescript-eslint/tsconfig-utils@npm:^8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.0" +"@typescript-eslint/tsconfig-utils@npm:8.34.1, @typescript-eslint/tsconfig-utils@npm:^8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.1" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/cbbca9526bd9c0309c77f9436f68c2c06712779a593a17757f1f7558ece27d9f40db2b37ebf12bd9e19cf227479083b7973c502436a0954a08406d8a598910ba + checksum: 10/81a874a433c4e91ee2509d4eda43932b8348e9404da2d11e621bf3b8bec26a6ab84bd3870215dcb09df950182e2b5e2539be30fc262c30edff0e42ca5d707465 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/type-utils@npm:8.34.0" +"@typescript-eslint/type-utils@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/type-utils@npm:8.34.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.34.0" - "@typescript-eslint/utils": "npm:8.34.0" + "@typescript-eslint/typescript-estree": "npm:8.34.1" + "@typescript-eslint/utils": "npm:8.34.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.1.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/e7c565868b18d66ce5de016455c5ba2dc625a845e05ad563bfdf08b1753faa11d9aef22b9dc5071c57b6e73932748505715e7b47993757f1bc244d4d6f70d688 + checksum: 10/1c8153a5b1cf488b6d1642d752caba8631f183f17031660859355342d1139e4dea9e0dd9c97d6bad644a91ee26461ddd1993303d0542e6f1b7850af1ca71e96e languageName: node linkType: hard -"@typescript-eslint/types@npm:8.34.0, @typescript-eslint/types@npm:^8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/types@npm:8.34.0" - checksum: 10/da4dcee51e78139bdeb5832df836528c519a22c2e39b7737ae660afe024576030165424079f423a131ad56e2dca8f033943d6b48a54b4f4d296a6f7f83f5b494 +"@typescript-eslint/types@npm:8.34.1, @typescript-eslint/types@npm:^8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/types@npm:8.34.1" + checksum: 10/09cb344af38e1e0f8e60968ff6038e8b27a453dea22be433b531e2b50b45448b8646f11249279d47e153f0a5299f8f621a84e81db8bcf5421bd90c94caae6416 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.34.0" +"@typescript-eslint/typescript-estree@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.34.1" dependencies: - "@typescript-eslint/project-service": "npm:8.34.0" - "@typescript-eslint/tsconfig-utils": "npm:8.34.0" - "@typescript-eslint/types": "npm:8.34.0" - "@typescript-eslint/visitor-keys": "npm:8.34.0" + "@typescript-eslint/project-service": "npm:8.34.1" + "@typescript-eslint/tsconfig-utils": "npm:8.34.1" + "@typescript-eslint/types": "npm:8.34.1" + "@typescript-eslint/visitor-keys": "npm:8.34.1" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -4979,32 +4979,32 @@ __metadata: ts-api-utils: "npm:^2.1.0" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/26817d4c948253eb6a8f49fcd7a8f74c4ffeae7943aef9e1cb90d1b7adbc8e0f66605b0b318dc6eee3eda212882e278a300776b26fe4e2319712cd9822a3a4e4 + checksum: 10/40ffa31d8005115fb8efe47eeea484ad8a32a55e8bc2e27e4ad7b89b9fb1b962254c4c4ec9c00b4a5d52c5fa45b25b69ef62a98135f478e486f51ea5ba0ad4e9 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/utils@npm:8.34.0" +"@typescript-eslint/utils@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/utils@npm:8.34.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.34.0" - "@typescript-eslint/types": "npm:8.34.0" - "@typescript-eslint/typescript-estree": "npm:8.34.0" + "@typescript-eslint/scope-manager": "npm:8.34.1" + "@typescript-eslint/types": "npm:8.34.1" + "@typescript-eslint/typescript-estree": "npm:8.34.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/c51d2015e8076dd2a9d8255746889130aaf298cf9ff8f73114dcf7148f34536d47d883880eec7e3d89ec3f746c2d3f2b749e8fef5e8ad9914132deb5c013efbd + checksum: 10/7e14ef16222d48aa668c2b436b7eec893e8baf05a18c4bcdf353fa6ce4b5526db3d3945be5a7bd4dab0202805f205c4a904cf8646fa157f53b761c090d9c5e7b languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.34.0": - version: 8.34.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.34.0" +"@typescript-eslint/visitor-keys@npm:8.34.1": + version: 8.34.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.34.1" dependencies: - "@typescript-eslint/types": "npm:8.34.0" - eslint-visitor-keys: "npm:^4.2.0" - checksum: 10/8a591cb9f922b6fd92107ebdf255425cf7ecd56281d032d944fb38e6be319e6cca7dc49bab6ad1d46390d2ca023c3413c03775e638ec5fd70172150debf7636a + "@typescript-eslint/types": "npm:8.34.1" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10/6fbaa838dc040c6ff6d4472b9a1480f1407eb591924fb4d371fe0224dafcb40ac5476b733fea33ad0898c3174430918b0456c5209b5b7e176cb04c0c9daacbd8 languageName: node linkType: hard @@ -8045,7 +8045,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^4.2.0, eslint-visitor-keys@npm:^4.2.1": +"eslint-visitor-keys@npm:^4.2.1": version: 4.2.1 resolution: "eslint-visitor-keys@npm:4.2.1" checksum: 10/3ee00fc6a7002d4b0ffd9dc99e13a6a7882c557329e6c25ab254220d71e5c9c4f89dca4695352949ea678eb1f3ba912a18ef8aac0a7fe094196fd92f441bfce2 @@ -9408,7 +9408,7 @@ __metadata: tinykeys: "npm:3.0.0" ts-lit-plugin: "npm:2.0.2" typescript: "npm:5.8.3" - typescript-eslint: "npm:8.34.0" + typescript-eslint: "npm:8.34.1" ua-parser-js: "npm:2.0.3" vis-data: "npm:7.1.9" vite-tsconfig-paths: "npm:5.1.4" @@ -14430,17 +14430,17 @@ __metadata: languageName: node linkType: hard -"typescript-eslint@npm:8.34.0": - version: 8.34.0 - resolution: "typescript-eslint@npm:8.34.0" +"typescript-eslint@npm:8.34.1": + version: 8.34.1 + resolution: "typescript-eslint@npm:8.34.1" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.34.0" - "@typescript-eslint/parser": "npm:8.34.0" - "@typescript-eslint/utils": "npm:8.34.0" + "@typescript-eslint/eslint-plugin": "npm:8.34.1" + "@typescript-eslint/parser": "npm:8.34.1" + "@typescript-eslint/utils": "npm:8.34.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/1c80c29ca341af2cb29aac0e80e3243b10f424e2d218bef0a536fc03d6a08c117e61a12ed9ab5a9ce45e236ab73754f727447aa3d08b5a30d76c2bf95d2408d2 + checksum: 10/78088abe01b7f6ba4c6036a43eb3992dfe16dc7604db73e0b9f3c7c4adb452ab715c4d644344ef89ee52c941f7536a290b22a09b0e35dcef2cf158c99b49b17d languageName: node linkType: hard From f87e20cae956a685643cafedb6c84f30cf8cfd0f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 20 Jun 2025 15:33:26 +0200 Subject: [PATCH 067/216] Redesign area card (#25802) * Use entity filter to get device classes in editor * Add name and sensor states to area card * Fix area type * Add basic controls * Fix editor * Add image * Add image type * Add translation key for area controls * Improve editor * Fix unknown entity id in area * Fix default feature position * Add alert badge * Add helper * Display all alerts when using big card * Disable covers and re-enable switches * Filter compatible controls * Use state icon for alerts * Rename to display type * Delete deprecated show camera * Fix aspect ratio * Improve helper * Undo domain icon changes * Undo domain icon changes * Update types * Fix translation cases * Fix card size * Feedback * Don't fallback to compact * Use plural form * Refactor active color --- src/components/ha-aspect-ratio.ts | 61 + .../hui-area-controls-card-feature.ts | 258 ++++ src/panels/lovelace/card-features/types.ts | 13 +- src/panels/lovelace/cards/hui-area-card.ts | 1112 ++++++++--------- src/panels/lovelace/cards/types.ts | 9 +- src/panels/lovelace/components/hui-image.ts | 5 +- .../create-card-feature-element.ts | 8 +- .../config-elements/hui-area-card-editor.ts | 457 +++++-- .../hui-area-controls-card-feature-editor.ts | 180 +++ .../hui-card-features-editor.ts | 4 + src/translations/en.json | 24 +- 11 files changed, 1449 insertions(+), 682 deletions(-) create mode 100644 src/components/ha-aspect-ratio.ts create mode 100644 src/panels/lovelace/card-features/hui-area-controls-card-feature.ts create mode 100644 src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts diff --git a/src/components/ha-aspect-ratio.ts b/src/components/ha-aspect-ratio.ts new file mode 100644 index 0000000000..67bc73c1e5 --- /dev/null +++ b/src/components/ha-aspect-ratio.ts @@ -0,0 +1,61 @@ +import { css, html, LitElement, type PropertyValues } from "lit"; +import { customElement, property } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import parseAspectRatio from "../common/util/parse-aspect-ratio"; + +const DEFAULT_ASPECT_RATIO = "16:9"; + +@customElement("ha-aspect-ratio") +export class HaAspectRatio extends LitElement { + @property({ type: String, attribute: "aspect-ratio" }) + public aspectRatio?: string; + + private _ratio: { + w: number; + h: number; + } | null = null; + + public willUpdate(changedProps: PropertyValues) { + if (changedProps.has("aspect_ratio") || this._ratio === null) { + this._ratio = this.aspectRatio + ? parseAspectRatio(this.aspectRatio) + : null; + + if (this._ratio === null || this._ratio.w <= 0 || this._ratio.h <= 0) { + this._ratio = parseAspectRatio(DEFAULT_ASPECT_RATIO); + } + } + } + + protected render(): unknown { + if (!this.aspectRatio) { + return html``; + } + return html` +
    + +
    + `; + } + + static styles = css` + .ratio ::slotted(*) { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-aspect-ratio": HaAspectRatio; + } +} diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts new file mode 100644 index 0000000000..ff6a8366a7 --- /dev/null +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -0,0 +1,258 @@ +import { mdiFan, mdiLightbulb, mdiToggleSwitch } from "@mdi/js"; +import { callService, type HassEntity } from "home-assistant-js-websocket"; +import { LitElement, css, html, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; +import memoizeOne from "memoize-one"; +import { + generateEntityFilter, + type EntityFilter, +} from "../../../common/entity/entity_filter"; +import { stateActive } from "../../../common/entity/state_active"; +import "../../../components/ha-control-button"; +import "../../../components/ha-control-button-group"; +import "../../../components/ha-svg-icon"; +import type { AreaRegistryEntry } from "../../../data/area_registry"; +import type { HomeAssistant } from "../../../types"; +import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types"; +import { cardFeatureStyles } from "./common/card-feature-styles"; +import type { + AreaControl, + AreaControlsCardFeatureConfig, + LovelaceCardFeatureContext, +} from "./types"; +import { AREA_CONTROLS } from "./types"; + +interface AreaControlsButton { + iconPath: string; + activeColor: string; + onService: string; + offService: string; + filter: EntityFilter; +} + +export const AREA_CONTROLS_BUTTONS: Record = { + light: { + iconPath: mdiLightbulb, + filter: { + domain: "light", + }, + activeColor: "var(--state-light-active-color)", + onService: "light.turn_on", + offService: "light.turn_off", + }, + fan: { + iconPath: mdiFan, + filter: { + domain: "fan", + }, + activeColor: "var(--state-fan-active-color)", + onService: "fan.turn_on", + offService: "fan.turn_off", + }, + switch: { + iconPath: mdiToggleSwitch, + filter: { + domain: "switch", + }, + activeColor: "var(--state-switch-active-color)", + onService: "switch.turn_on", + offService: "switch.turn_off", + }, +}; + +export const supportsAreaControlsCardFeature = ( + hass: HomeAssistant, + context: LovelaceCardFeatureContext +) => { + const area = context.area_id ? hass.areas[context.area_id] : undefined; + return !!area; +}; + +export const getAreaControlEntities = ( + controls: AreaControl[], + areaId: string, + hass: HomeAssistant +): Record => + controls.reduce( + (acc, control) => { + const controlButton = AREA_CONTROLS_BUTTONS[control]; + const filter = generateEntityFilter(hass, { + area: areaId, + ...controlButton.filter, + }); + + acc[control] = Object.keys(hass.entities).filter((entityId) => + filter(entityId) + ); + return acc; + }, + {} as Record + ); + +@customElement("hui-area-controls-card-feature") +class HuiAreaControlsCardFeature + extends LitElement + implements LovelaceCardFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public context?: LovelaceCardFeatureContext; + + @state() private _config?: AreaControlsCardFeatureConfig; + + private get _area() { + if (!this.hass || !this.context || !this.context.area_id) { + return undefined; + } + return this.hass.areas[this.context.area_id!] as + | AreaRegistryEntry + | undefined; + } + + private get _controls() { + return ( + this._config?.controls || (AREA_CONTROLS as unknown as AreaControl[]) + ); + } + + static getStubConfig(): AreaControlsCardFeatureConfig { + return { + type: "area-controls", + }; + } + + public static async getConfigElement(): Promise { + await import( + "../editor/config-elements/hui-area-controls-card-feature-editor" + ); + return document.createElement("hui-area-controls-card-feature-editor"); + } + + public setConfig(config: AreaControlsCardFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + private _handleButtonTap(ev: MouseEvent) { + ev.stopPropagation(); + + if (!this.context?.area_id || !this.hass) { + return; + } + const control = (ev.currentTarget as any).control as AreaControl; + + const controlEntities = this._controlEntities( + this._controls, + this.context.area_id, + this.hass!.entities, + this.hass!.devices, + this.hass!.areas + ); + const entitiesIds = controlEntities[control]; + + const { onService, offService } = AREA_CONTROLS_BUTTONS[control]; + + const isOn = entitiesIds.some((entityId) => + stateActive(this.hass!.states[entityId] as HassEntity) + ); + + const [domain, service] = (isOn ? offService : onService).split("."); + + callService(this.hass!.connection, domain, service, { + entity_id: entitiesIds, + }); + } + + private _controlEntities = memoizeOne( + ( + controls: AreaControl[], + areaId: string, + // needed to update memoized function when entities, devices or areas change + _entities: HomeAssistant["entities"], + _devices: HomeAssistant["devices"], + _areas: HomeAssistant["areas"] + ) => getAreaControlEntities(controls, areaId, this.hass!) + ); + + protected render() { + if ( + !this._config || + !this.hass || + !this.context || + !this._area || + !supportsAreaControlsCardFeature(this.hass, this.context) + ) { + return nothing; + } + + const controlEntities = this._controlEntities( + this._controls, + this.context.area_id!, + this.hass!.entities, + this.hass!.devices, + this.hass!.areas + ); + + const supportedControls = this._controls.filter( + (control) => controlEntities[control].length > 0 + ); + + if (!supportedControls.length) { + return nothing; + } + + return html` + + ${supportedControls.map((control) => { + const button = AREA_CONTROLS_BUTTONS[control]; + + const entities = controlEntities[control]; + const active = entities.some((entityId) => { + const stateObj = this.hass!.states[entityId] as + | HassEntity + | undefined; + if (!stateObj) { + return false; + } + return stateActive(stateObj); + }); + + return html` + + + + `; + })} + + `; + } + + static get styles() { + return [ + cardFeatureStyles, + css` + ha-control-button { + --active-color: var(--primary-color); + } + ha-control-button.active { + --control-button-background-color: var(--active-color); + --control-button-icon-color: var(--active-color); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-area-controls-card-feature": HuiAreaControlsCardFeature; + } +} diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index 743f1a607f..bb0852955b 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -158,6 +158,15 @@ export interface UpdateActionsCardFeatureConfig { backup?: "yes" | "no" | "ask"; } +export const AREA_CONTROLS = ["light", "fan", "switch"] as const; + +export type AreaControl = (typeof AREA_CONTROLS)[number]; + +export interface AreaControlsCardFeatureConfig { + type: "area-controls"; + controls?: AreaControl[]; +} + export type LovelaceCardFeatureConfig = | AlarmModesCardFeatureConfig | ClimateFanModesCardFeatureConfig @@ -187,8 +196,10 @@ export type LovelaceCardFeatureConfig = | ToggleCardFeatureConfig | UpdateActionsCardFeatureConfig | VacuumCommandsCardFeatureConfig - | WaterHeaterOperationModesCardFeatureConfig; + | WaterHeaterOperationModesCardFeatureConfig + | AreaControlsCardFeatureConfig; export interface LovelaceCardFeatureContext { entity_id?: string; + area_id?: string; } diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index 0b390386b0..5c8eb2888e 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -1,23 +1,19 @@ +import { mdiTextureBox } from "@mdi/js"; +import type { HassEntity } from "home-assistant-js-websocket"; import { - mdiFan, - mdiFanOff, - mdiLightbulbMultiple, - mdiLightbulbMultipleOff, - mdiRun, - mdiToggleSwitch, - mdiToggleSwitchOff, - mdiWaterAlert, -} from "@mdi/js"; -import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket"; -import type { PropertyValues, TemplateResult } from "lit"; -import { LitElement, css, html, nothing } from "lit"; + css, + html, + LitElement, + nothing, + type PropertyValues, + type TemplateResult, +} from "lit"; import { customElement, property, state } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; -import { styleMap } from "lit/directives/style-map"; +import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; -import { STATES_OFF } from "../../../common/const"; -import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; -import { computeDomain } from "../../../common/entity/compute_domain"; +import { BINARY_STATE_ON } from "../../../common/const"; +import { computeAreaName } from "../../../common/entity/compute_area_name"; +import { generateEntityFilter } from "../../../common/entity/entity_filter"; import { navigate } from "../../../common/navigate"; import { formatNumber, @@ -25,23 +21,22 @@ import { } from "../../../common/number/format_number"; import { blankBeforeUnit } from "../../../common/translations/blank_before_unit"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; -import { subscribeOne } from "../../../common/util/subscribe-one"; +import "../../../components/ha-aspect-ratio"; import "../../../components/ha-card"; +import "../../../components/ha-control-button"; +import "../../../components/ha-control-button-group"; import "../../../components/ha-domain-icon"; -import "../../../components/ha-icon-button"; -import "../../../components/ha-state-icon"; -import type { AreaRegistryEntry } from "../../../data/area_registry"; -import { subscribeAreaRegistry } from "../../../data/area_registry"; -import type { DeviceRegistryEntry } from "../../../data/device_registry"; -import { subscribeDeviceRegistry } from "../../../data/device_registry"; +import "../../../components/ha-icon"; +import "../../../components/ha-ripple"; +import "../../../components/ha-svg-icon"; +import "../../../components/tile/ha-tile-badge"; +import "../../../components/tile/ha-tile-icon"; +import "../../../components/tile/ha-tile-info"; import { isUnavailableState } from "../../../data/entity"; -import type { EntityRegistryEntry } from "../../../data/entity_registry"; -import { subscribeEntityRegistry } from "../../../data/entity_registry"; -import { forwardHaptic } from "../../../data/haptics"; -import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import type { HomeAssistant } from "../../../types"; -import "../components/hui-image"; -import "../components/hui-warning"; +import "../card-features/hui-card-features"; +import type { LovelaceCardFeatureContext } from "../card-features/types"; +import { actionHandler } from "../common/directives/action-handler-directive"; import type { LovelaceCard, LovelaceCardEditor, @@ -51,284 +46,338 @@ import type { AreaCardConfig } from "./types"; export const DEFAULT_ASPECT_RATIO = "16:9"; -const SENSOR_DOMAINS = ["sensor"]; - -const ALERT_DOMAINS = ["binary_sensor"]; - -const TOGGLE_DOMAINS = ["light", "switch", "fan"]; - -const OTHER_DOMAINS = ["camera"]; - export const DEVICE_CLASSES = { sensor: ["temperature", "humidity"], binary_sensor: ["motion", "moisture"], }; -const DOMAIN_ICONS = { - light: { on: mdiLightbulbMultiple, off: mdiLightbulbMultipleOff }, - switch: { on: mdiToggleSwitch, off: mdiToggleSwitchOff }, - fan: { on: mdiFan, off: mdiFanOff }, - binary_sensor: { - motion: mdiRun, - moisture: mdiWaterAlert, - }, -}; - @customElement("hui-area-card") -export class HuiAreaCard - extends SubscribeMixin(LitElement) - implements LovelaceCard -{ - public static async getConfigElement(): Promise { - await import("../editor/config-elements/hui-area-card-editor"); - return document.createElement("hui-area-card-editor"); - } - - public static async getStubConfig( - hass: HomeAssistant - ): Promise { - const areas = await subscribeOne(hass.connection, subscribeAreaRegistry); - return { type: "area", area: areas[0]?.area_id || "" }; - } - +export class HuiAreaCard extends LitElement implements LovelaceCard { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public layout?: string; @state() private _config?: AreaCardConfig; - @state() private _entities?: EntityRegistryEntry[]; - - @state() private _devices?: DeviceRegistryEntry[]; - - @state() private _areas?: AreaRegistryEntry[]; - - private _deviceClasses: Record = DEVICE_CLASSES; + @state() private _featureContext: LovelaceCardFeatureContext = {}; private _ratio: { w: number; h: number; } | null = null; - private _entitiesByDomain = memoizeOne( - ( - areaId: string, - devicesInArea: Set, - registryEntities: EntityRegistryEntry[], - deviceClasses: Record, - states: HomeAssistant["states"] - ) => { - const entitiesInArea = registryEntities - .filter( - (entry) => - !entry.entity_category && - !entry.hidden_by && - (entry.area_id - ? entry.area_id === areaId - : entry.device_id && devicesInArea.has(entry.device_id)) - ) - .map((entry) => entry.entity_id); - - const entitiesByDomain: Record = {}; - - for (const entity of entitiesInArea) { - const domain = computeDomain(entity); - if ( - !TOGGLE_DOMAINS.includes(domain) && - !SENSOR_DOMAINS.includes(domain) && - !ALERT_DOMAINS.includes(domain) && - !OTHER_DOMAINS.includes(domain) - ) { - continue; - } - const stateObj: HassEntity | undefined = states[entity]; - - if (!stateObj) { - continue; - } - - if ( - (SENSOR_DOMAINS.includes(domain) || ALERT_DOMAINS.includes(domain)) && - !deviceClasses[domain].includes( - stateObj.attributes.device_class || "" - ) - ) { - continue; - } - - if (!(domain in entitiesByDomain)) { - entitiesByDomain[domain] = []; - } - entitiesByDomain[domain].push(stateObj); - } - - return entitiesByDomain; - } - ); - - private _isOn(domain: string, deviceClass?: string): HassEntity | undefined { - const entities = this._entitiesByDomain( - this._config!.area, - this._devicesInArea(this._config!.area, this._devices!), - this._entities!, - this._deviceClasses, - this.hass.states - )[domain]; - if (!entities) { - return undefined; - } - return ( - deviceClass - ? entities.filter( - (entity) => entity.attributes.device_class === deviceClass - ) - : entities - ).find( - (entity) => - !isUnavailableState(entity.state) && !STATES_OFF.includes(entity.state) - ); - } - - private _average(domain: string, deviceClass?: string): string | undefined { - const entities = this._entitiesByDomain( - this._config!.area, - this._devicesInArea(this._config!.area, this._devices!), - this._entities!, - this._deviceClasses, - this.hass.states - )[domain].filter((entity) => - deviceClass ? entity.attributes.device_class === deviceClass : true - ); - if (!entities) { - return undefined; - } - let uom; - const values = entities.filter((entity) => { - if (!isNumericState(entity) || isNaN(Number(entity.state))) { - return false; - } - if (!uom) { - uom = entity.attributes.unit_of_measurement; - return true; - } - return entity.attributes.unit_of_measurement === uom; - }); - if (!values.length) { - return undefined; - } - const sum = values.reduce( - (total, entity) => total + Number(entity.state), - 0 - ); - return `${formatNumber(sum / values.length, this.hass!.locale, { - maximumFractionDigits: 1, - })}${uom ? blankBeforeUnit(uom, this.hass!.locale) : ""}${uom || ""}`; - } - - private _area = memoizeOne( - (areaId: string | undefined, areas: AreaRegistryEntry[]) => - areas.find((area) => area.area_id === areaId) || null - ); - - private _devicesInArea = memoizeOne( - (areaId: string | undefined, devices: DeviceRegistryEntry[]) => - new Set( - areaId - ? devices - .filter((device) => device.area_id === areaId) - .map((device) => device.id) - : [] - ) - ); - - public hassSubscribe(): UnsubscribeFunc[] { - return [ - subscribeAreaRegistry(this.hass!.connection, (areas) => { - this._areas = areas; - }), - subscribeDeviceRegistry(this.hass!.connection, (devices) => { - this._devices = devices; - }), - subscribeEntityRegistry(this.hass!.connection, (entries) => { - this._entities = entries; - }), - ]; - } - - public getCardSize(): number { - return 3; + public static async getConfigElement(): Promise { + await import("../editor/config-elements/hui-area-card-editor"); + return document.createElement("hui-area-card-editor"); } public setConfig(config: AreaCardConfig): void { if (!config.area) { - throw new Error("Area Required"); + throw new Error("Specify an area"); } - this._config = config; + const displayType = + config.display_type || (config.show_camera ? "camera" : "picture"); + this._config = { + ...config, + display_type: displayType, + }; - this._deviceClasses = { ...DEVICE_CLASSES }; - if (config.sensor_classes) { - this._deviceClasses.sensor = config.sensor_classes; - } - if (config.alert_classes) { - this._deviceClasses.binary_sensor = config.alert_classes; - } + this._featureContext = { + area_id: config.area, + }; } - protected shouldUpdate(changedProps: PropertyValues): boolean { - if (changedProps.has("_config") || !this._config) { - return true; - } + public static async getStubConfig( + hass: HomeAssistant + ): Promise { + const areas = Object.values(hass.areas); + return { type: "area", area: areas[0]?.area_id || "" }; + } - if ( - changedProps.has("_devicesInArea") || - changedProps.has("_areas") || - changedProps.has("_entities") - ) { - return true; - } - - if (!changedProps.has("hass")) { - return false; - } - - const oldHass = changedProps.get("hass") as HomeAssistant | undefined; - - if ( - !oldHass || - oldHass.themes !== this.hass!.themes || - oldHass.locale !== this.hass!.locale - ) { - return true; - } - - if ( - !this._devices || - !this._devicesInArea(this._config.area, this._devices) || - !this._entities - ) { - return false; - } - - const entities = this._entitiesByDomain( - this._config.area, - this._devicesInArea(this._config.area, this._devices), - this._entities, - this._deviceClasses, - this.hass.states + public getCardSize(): number { + const featuresPosition = + this._config && this._featurePosition(this._config); + const displayType = this._config?.display_type || "picture"; + const featuresCount = this._config?.features?.length || 0; + return ( + 1 + + (displayType === "compact" ? 0 : 2) + + (featuresPosition === "inline" ? 0 : featuresCount) ); + } - for (const domainEntities of Object.values(entities)) { - for (const stateObj of domainEntities) { - if (oldHass!.states[stateObj.entity_id] !== stateObj) { - return true; - } + public getGridOptions(): LovelaceGridOptions { + const columns = 6; + let min_columns = 6; + let rows = 1; + const featurePosition = this._config + ? this._featurePosition(this._config) + : "bottom"; + const featuresCount = this._config?.features?.length || 0; + if (featuresCount) { + if (featurePosition === "inline") { + min_columns = 12; + } else { + rows += featuresCount; } } - return false; + const displayType = this._config?.display_type || "picture"; + + if (displayType !== "compact") { + rows += 2; + } + + return { + columns, + rows, + min_columns, + min_rows: rows, + }; } + private get _hasCardAction() { + return this._config?.navigation_path; + } + + private _handleAction() { + if (this._config?.navigation_path) { + navigate(this._config.navigation_path); + } + } + + private _groupEntitiesByDeviceClass = ( + entityIds: string[] + ): Map => + entityIds.reduce((acc, entityId) => { + const stateObj = this.hass.states[entityId]; + const deviceClass = stateObj.attributes.device_class!; + if (!acc.has(deviceClass)) { + acc.set(deviceClass, []); + } + acc.get(deviceClass)!.push(stateObj.entity_id); + return acc; + }, new Map()); + + private _groupedSensorEntityIds = memoizeOne( + ( + entities: HomeAssistant["entities"], + areaId: string, + sensorClasses: string[] + ): Map => { + const sensorFilter = generateEntityFilter(this.hass, { + area: areaId, + entity_category: "none", + domain: "sensor", + device_class: sensorClasses, + }); + const entityIds = Object.keys(entities).filter(sensorFilter); + return this._groupEntitiesByDeviceClass(entityIds); + } + ); + + private _groupedBinarySensorEntityIds = memoizeOne( + ( + entities: HomeAssistant["entities"], + areaId: string, + binarySensorClasses: string[] + ): Map => { + const binarySensorFilter = generateEntityFilter(this.hass, { + area: areaId, + entity_category: "none", + domain: "binary_sensor", + device_class: binarySensorClasses, + }); + const entityIds = Object.keys(entities).filter(binarySensorFilter); + return this._groupEntitiesByDeviceClass(entityIds); + } + ); + + private _getCameraEntity = memoizeOne( + ( + entities: HomeAssistant["entities"], + areaId: string + ): string | undefined => { + const cameraFilter = generateEntityFilter(this.hass, { + area: areaId, + entity_category: "none", + domain: "camera", + }); + const cameraEntities = Object.keys(entities).filter(cameraFilter); + return cameraEntities.length > 0 ? cameraEntities[0] : undefined; + } + ); + + private _computeActiveAlertStates(): HassEntity[] { + const areaId = this._config?.area; + const area = areaId ? this.hass.areas[areaId] : undefined; + const alertClasses = this._config?.alert_classes; + if (!area || !alertClasses) { + return []; + } + const groupedEntities = this._groupedBinarySensorEntityIds( + this.hass.entities, + area.area_id, + alertClasses + ); + + return ( + alertClasses + .map((alertClass) => { + const entityIds = groupedEntities.get(alertClass) || []; + if (!entityIds) { + return []; + } + return entityIds + .map( + (entityId) => this.hass.states[entityId] as HassEntity | undefined + ) + .filter((stateObj) => stateObj?.state === BINARY_STATE_ON); + }) + .filter((activeAlerts) => activeAlerts.length > 0) + // Only return the first active entity for each alert class + .map((activeAlerts) => activeAlerts[0]!) + ); + } + + private _renderAlertSensorBadge(): TemplateResult<1> | typeof nothing { + const states = this._computeActiveAlertStates(); + + if (states.length === 0) { + return nothing; + } + + // Only render the first one when using a badge + const stateObj = states[0] as HassEntity | undefined; + + return html` + + + + `; + } + + private _renderAlertSensors(): TemplateResult<1> | typeof nothing { + const states = this._computeActiveAlertStates(); + + if (states.length === 0) { + return nothing; + } + return html` +
    + ${states.map( + (stateObj) => html` +
    + +
    + ` + )} +
    + `; + } + + private _computeSensorsDisplay(): string | undefined { + const areaId = this._config?.area; + const area = areaId ? this.hass.areas[areaId] : undefined; + const sensorClasses = this._config?.sensor_classes; + if (!area || !sensorClasses) { + return undefined; + } + + const groupedEntities = this._groupedSensorEntityIds( + this.hass.entities, + area.area_id, + sensorClasses + ); + + const sensorStates = sensorClasses + .map((sensorClass) => { + if (sensorClass === "temperature" && area.temperature_entity_id) { + const stateObj = this.hass.states[area.temperature_entity_id] as + | HassEntity + | undefined; + return !stateObj || isUnavailableState(stateObj.state) + ? "" + : this.hass.formatEntityState(stateObj); + } + if (sensorClass === "humidity" && area.humidity_entity_id) { + const stateObj = this.hass.states[area.humidity_entity_id] as + | HassEntity + | undefined; + return !stateObj || isUnavailableState(stateObj.state) + ? "" + : this.hass.formatEntityState(stateObj); + } + + const entityIds = groupedEntities.get(sensorClass); + + if (!entityIds) { + return undefined; + } + + // Ensure all entities have state + const entities = entityIds + .map((entityId) => this.hass.states[entityId]) + .filter(Boolean); + + if (entities.length === 0) { + return undefined; + } + + // Use the first entity's unit_of_measurement for formatting + const uom = entities.find( + (entity) => entity.attributes.unit_of_measurement + )?.attributes.unit_of_measurement; + + // Ensure all entities have the same unit_of_measurement + const validEntities = entities.filter( + (entity) => + entity.attributes.unit_of_measurement === uom && + isNumericState(entity) && + !isNaN(Number(entity.state)) + ); + + if (validEntities.length === 0) { + return undefined; + } + + const value = + validEntities.reduce((acc, entity) => acc + Number(entity.state), 0) / + validEntities.length; + + const formattedAverage = formatNumber(value, this.hass!.locale, { + maximumFractionDigits: 1, + }); + const formattedUnit = uom + ? `${blankBeforeUnit(uom, this.hass!.locale)}${uom}` + : ""; + + return `${formattedAverage}${formattedUnit}`; + }) + .filter(Boolean) + .join(" · "); + + return sensorStates; + } + + private _featurePosition = memoizeOne( + (config: AreaCardConfig) => config.features_position || "bottom" + ); + + private _displayedFeatures = memoizeOne((config: AreaCardConfig) => { + const features = config.features || []; + const featurePosition = this._featurePosition(config); + + if (featurePosition === "inline") { + return features.slice(0, 1); + } + return features; + }); + public willUpdate(changedProps: PropertyValues) { if (changedProps.has("_config") || this._ratio === null) { this._ratio = this._config?.aspect_ratio @@ -342,26 +391,14 @@ export class HuiAreaCard } protected render() { - if ( - !this._config || - !this.hass || - !this._areas || - !this._devices || - !this._entities - ) { + if (!this._config || !this.hass) { return nothing; } - const entitiesByDomain = this._entitiesByDomain( - this._config.area, - this._devicesInArea(this._config.area, this._devices), - this._entities, - this._deviceClasses, - this.hass.states - ); - const area = this._area(this._config.area, this._areas); + const areaId = this._config?.area; + const area = areaId ? this.hass.areas[areaId] : undefined; - if (area === null) { + if (!area) { return html` ${this.hass.localize("ui.card.area.area_not_found")} @@ -369,315 +406,270 @@ export class HuiAreaCard `; } - const sensors: TemplateResult[] = []; - SENSOR_DOMAINS.forEach((domain) => { - if (!(domain in entitiesByDomain)) { - return; - } - this._deviceClasses[domain].forEach((deviceClass) => { - let areaSensorEntityId: string | null = null; - switch (deviceClass) { - case "temperature": - areaSensorEntityId = area.temperature_entity_id; - break; - case "humidity": - areaSensorEntityId = area.humidity_entity_id; - break; - } - const areaEntity = - areaSensorEntityId && - this.hass.states[areaSensorEntityId] && - !isUnavailableState(this.hass.states[areaSensorEntityId].state) - ? this.hass.states[areaSensorEntityId] - : undefined; - if ( - areaEntity || - entitiesByDomain[domain].some( - (entity) => entity.attributes.device_class === deviceClass - ) - ) { - let value = areaEntity - ? this.hass.formatEntityState(areaEntity) - : this._average(domain, deviceClass); - if (!value) value = "—"; - sensors.push(html` -
    - - ${value} -
    - `); - } - }); - }); + const icon = area.icon; - let cameraEntityId: string | undefined; - if (this._config.show_camera && "camera" in entitiesByDomain) { - cameraEntityId = entitiesByDomain.camera[0].entity_id; - } + const name = this._config.name || computeAreaName(area); - const imageClass = area.picture || cameraEntityId; + const primary = name; + const secondary = this._computeSensorsDisplay(); - const ignoreAspectRatio = this.layout === "grid"; + const featurePosition = this._featurePosition(this._config); + const features = this._displayedFeatures(this._config); + + const containerOrientationClass = + featurePosition === "inline" ? "horizontal" : ""; + + const displayType = this._config.display_type || "picture"; + + const cameraEntityId = + displayType === "camera" + ? this._getCameraEntity(this.hass.entities, area.area_id) + : undefined; + + const ignoreAspectRatio = this.layout === "grid" || this.layout === "panel"; return html` - - ${area.picture || cameraEntityId - ? html` - - ` - : area.icon - ? html` -
    - + +
    + +
    + ${displayType === "compact" + ? nothing + : html` +
    +
    + ${(displayType === "picture" || displayType === "camera") && + (cameraEntityId || area.picture) + ? html` + + ` + : html` + +
    + ${area.icon + ? html`` + : nothing} +
    +
    + `}
    + ${this._renderAlertSensors()} +
    + `} +
    +
    + + ${displayType === "compact" + ? this._renderAlertSensorBadge() + : nothing} + ${icon + ? html`` + : html` + + `} + + +
    + ${features.length > 0 + ? html` + ` : nothing} - - `; } - protected updated(changedProps: PropertyValues): void { - super.updated(changedProps); - if (!this._config || !this.hass) { - return; - } - const oldHass = changedProps.get("hass") as HomeAssistant | undefined; - const oldConfig = changedProps.get("_config") as AreaCardConfig | undefined; - - if ( - (changedProps.has("hass") && - (!oldHass || oldHass.themes !== this.hass.themes)) || - (changedProps.has("_config") && - (!oldConfig || oldConfig.theme !== this._config.theme)) - ) { - applyThemesOnElement(this, this.hass.themes, this._config.theme); - } - } - - private _handleNavigation() { - if (this._config!.navigation_path) { - navigate(this._config!.navigation_path); - } - } - - private _toggle(ev: Event) { - ev.stopPropagation(); - const domain = (ev.currentTarget as any).domain as string; - if (TOGGLE_DOMAINS.includes(domain)) { - this.hass.callService( - domain, - this._isOn(domain) ? "turn_off" : "turn_on", - undefined, - { - area_id: this._config!.area, - } - ); - } - forwardHaptic("light"); - } - - getGridOptions(): LovelaceGridOptions { - return { - columns: 12, - rows: 3, - min_columns: 3, - }; - } - static styles = css` - ha-card { - overflow: hidden; - position: relative; - background-size: cover; - height: 100%; + :host { + --tile-color: var(--state-icon-color); + -webkit-tap-highlight-color: transparent; } - - .container { + ha-card:has(.background:focus-visible) { + --shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent); + --shadow-focus: 0 0 0 1px var(--tile-color); + border-color: var(--tile-color); + box-shadow: var(--shadow-default), var(--shadow-focus); + } + ha-card { + --ha-ripple-color: var(--tile-color); + --ha-ripple-hover-opacity: 0.04; + --ha-ripple-pressed-opacity: 0.12; + height: 100%; + transition: + box-shadow 180ms ease-in-out, + border-color 180ms ease-in-out; display: flex; flex-direction: column; justify-content: space-between; + } + [role="button"] { + cursor: pointer; + pointer-events: auto; + } + [role="button"]:focus { + outline: none; + } + .background { position: absolute; top: 0; - bottom: 0; left: 0; + bottom: 0; right: 0; - background: linear-gradient( - 0, - rgba(33, 33, 33, 0.9) 0%, - rgba(33, 33, 33, 0) 45% - ); + border-radius: var(--ha-card-border-radius, 12px); + margin: calc(-1 * var(--ha-card-border-width, 1px)); + overflow: hidden; } - - ha-card:not(.image) .container::before { + .header { + flex: 1; + overflow: hidden; + border-radius: var(--ha-card-border-radius, 12px); + border-end-end-radius: 0; + border-end-start-radius: 0; + pointer-events: none; + } + .picture { + height: 100%; + width: 100%; + background-size: cover; + background-position: center; + position: relative; + } + .picture hui-image { + height: 100%; + } + .picture .icon-container { + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + --mdc-icon-size: 48px; + color: var(--tile-color); + } + .picture .icon-container::before { position: absolute; content: ""; width: 100%; height: 100%; - background-color: var(--sidebar-selected-icon-color); + background-color: var(--tile-color); opacity: 0.12; } - - .image hui-image { - height: 100%; + .container { + margin: calc(-1 * var(--ha-card-border-width, 1px)); + display: flex; + flex-direction: column; + flex: 1; + } + .header + .container { + height: auto; + flex: none; + } + .container.horizontal { + flex-direction: row; } - .icon-container { + .content { + position: relative; + display: flex; + flex-direction: row; + align-items: center; + padding: 10px; + flex: 1; + min-width: 0; + box-sizing: border-box; + pointer-events: none; + gap: 10px; + } + + ha-tile-icon { + --tile-icon-color: var(--tile-color); + position: relative; + padding: 6px; + margin: -6px; + } + ha-tile-badge { + position: absolute; + top: 3px; + right: 3px; + inset-inline-end: 3px; + inset-inline-start: initial; + } + ha-tile-info { + position: relative; + min-width: 0; + transition: background-color 180ms ease-in-out; + box-sizing: border-box; + } + hui-card-features { + --feature-color: var(--tile-color); + padding: 0 12px 12px 12px; + } + .container.horizontal hui-card-features { + width: calc(50% - var(--column-gap, 0px) / 2 - 12px); + flex: none; + --feature-height: 36px; + padding: 0 12px; + padding-inline-start: 0; + } + .alert-badge { + --tile-badge-background-color: var(--orange-color); + } + .alerts { position: absolute; top: 0; left: 0; - right: 0; - bottom: 0; + display: flex; + flex-direction: row; + gap: 4px; + padding: 4px; + pointer-events: none; + z-index: 1; + } + .alert { + background-color: var(--orange-color); + border-radius: 12px; + width: 24px; + height: 24px; + padding: 2px; + box-sizing: border-box; + --mdc-icon-size: 16px; display: flex; align-items: center; justify-content: center; - } - - .icon-container ha-icon { - --mdc-icon-size: 60px; - color: var(--sidebar-selected-icon-color); - } - - .sensors { - color: #e3e3e3; - font-size: var(--ha-font-size-l); - --mdc-icon-size: 24px; - opacity: 0.6; - margin-top: 8px; - } - - .sensor { - white-space: nowrap; - float: left; - margin-right: 4px; - margin-inline-end: 4px; - margin-inline-start: initial; - } - - .alerts { - padding: 16px; - } - - ha-state-icon { - display: inline-flex; - align-items: center; - justify-content: center; - position: relative; - } - - .alerts ha-state-icon { - background: var(--accent-color); - color: var(--text-accent-color, var(--text-primary-color)); - padding: 8px; - margin-right: 8px; - margin-inline-end: 8px; - margin-inline-start: initial; - border-radius: 50%; - } - - .name { color: white; - font-size: var(--ha-font-size-2xl); - } - - .bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding: 16px; - } - - .navigate { - cursor: pointer; - } - - ha-icon-button { - color: white; - background-color: var(--area-button-color, #727272b2); - border-radius: 50%; - margin-left: 8px; - margin-inline-start: 8px; - margin-inline-end: initial; - --mdc-icon-button-size: 44px; - } - .on { - color: var(--state-light-active-color); } `; } diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 52466b2de8..0957c9289a 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -101,11 +101,18 @@ export interface EntitiesCardConfig extends LovelaceCardConfig { } export interface AreaCardConfig extends LovelaceCardConfig { - area: string; + area?: string; + name?: string; navigation_path?: string; + display_type?: "compact" | "icon" | "picture" | "camera"; + /** @deprecated Use `display_type` instead */ show_camera?: boolean; camera_view?: HuiImage["cameraView"]; aspect_ratio?: string; + sensor_classes?: string[]; + alert_classes?: string[]; + features?: LovelaceCardFeatureConfig[]; + features_position?: "bottom" | "inline"; } export interface ButtonCardConfig extends LovelaceCardConfig { diff --git a/src/panels/lovelace/components/hui-image.ts b/src/panels/lovelace/components/hui-image.ts index 08c54b61bf..35ba02cd77 100644 --- a/src/panels/lovelace/components/hui-image.ts +++ b/src/panels/lovelace/components/hui-image.ts @@ -54,7 +54,10 @@ export class HuiImage extends LitElement { @property({ attribute: false }) public darkModeFilter?: string; - @property({ attribute: false }) public fitMode?: "cover" | "contain" | "fill"; + @property({ attribute: "fit-mode", type: String }) public fitMode?: + | "cover" + | "contain" + | "fill"; @state() private _imageVisible? = false; diff --git a/src/panels/lovelace/create-element/create-card-feature-element.ts b/src/panels/lovelace/create-element/create-card-feature-element.ts index 31ce2eccd5..fc003da1f9 100644 --- a/src/panels/lovelace/create-element/create-card-feature-element.ts +++ b/src/panels/lovelace/create-element/create-card-feature-element.ts @@ -1,9 +1,9 @@ import "../card-features/hui-alarm-modes-card-feature"; import "../card-features/hui-climate-fan-modes-card-feature"; -import "../card-features/hui-climate-swing-modes-card-feature"; -import "../card-features/hui-climate-swing-horizontal-modes-card-feature"; import "../card-features/hui-climate-hvac-modes-card-feature"; import "../card-features/hui-climate-preset-modes-card-feature"; +import "../card-features/hui-climate-swing-horizontal-modes-card-feature"; +import "../card-features/hui-climate-swing-modes-card-feature"; import "../card-features/hui-counter-actions-card-feature"; import "../card-features/hui-cover-open-close-card-feature"; import "../card-features/hui-cover-position-card-feature"; @@ -21,12 +21,13 @@ import "../card-features/hui-lock-open-door-card-feature"; import "../card-features/hui-media-player-volume-slider-card-feature"; import "../card-features/hui-numeric-input-card-feature"; import "../card-features/hui-select-options-card-feature"; -import "../card-features/hui-target-temperature-card-feature"; import "../card-features/hui-target-humidity-card-feature"; +import "../card-features/hui-target-temperature-card-feature"; import "../card-features/hui-toggle-card-feature"; import "../card-features/hui-update-actions-card-feature"; import "../card-features/hui-vacuum-commands-card-feature"; import "../card-features/hui-water-heater-operation-modes-card-feature"; +import "../card-features/hui-area-controls-card-feature"; import type { LovelaceCardFeatureConfig } from "../card-features/types"; import { @@ -36,6 +37,7 @@ import { const TYPES = new Set([ "alarm-modes", + "area-controls", "climate-fan-modes", "climate-swing-modes", "climate-swing-horizontal-modes", diff --git a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts index 7248d9fdc3..e9c15e329c 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts @@ -1,43 +1,59 @@ -import { html, LitElement, nothing } from "lit"; +import { mdiGestureTap, mdiListBox, mdiTextShort } from "@mdi/js"; +import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { - assert, + any, array, + assert, assign, boolean, + enums, object, optional, string, } from "superstruct"; -import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-form/ha-form"; import { - DEFAULT_ASPECT_RATIO, - DEVICE_CLASSES, -} from "../../cards/hui-area-card"; -import type { SchemaUnion } from "../../../../components/ha-form/types"; + fireEvent, + type HASSDomEvent, +} from "../../../../common/dom/fire_event"; +import { generateEntityFilter } from "../../../../common/entity/entity_filter"; +import { caseInsensitiveStringCompare } from "../../../../common/string/compare"; +import type { LocalizeFunc } from "../../../../common/translations/localize"; +import "../../../../components/ha-form/ha-form"; +import type { + HaFormSchema, + SchemaUnion, +} from "../../../../components/ha-form/types"; +import type { SelectOption } from "../../../../data/selector"; +import { getSensorNumericDeviceClasses } from "../../../../data/sensor"; import type { HomeAssistant } from "../../../../types"; +import type { + LovelaceCardFeatureConfig, + LovelaceCardFeatureContext, +} from "../../card-features/types"; +import { DEVICE_CLASSES } from "../../cards/hui-area-card"; import type { AreaCardConfig } from "../../cards/types"; import type { LovelaceCardEditor } from "../../types"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; -import { computeDomain } from "../../../../common/entity/compute_domain"; -import { caseInsensitiveStringCompare } from "../../../../common/string/compare"; -import type { SelectOption } from "../../../../data/selector"; -import { getSensorNumericDeviceClasses } from "../../../../data/sensor"; -import type { LocalizeFunc } from "../../../../common/translations/localize"; +import type { EditDetailElementEvent, EditSubElementEvent } from "../types"; +import { configElementStyle } from "./config-elements-style"; +import { getSupportedFeaturesType } from "./hui-card-features-editor"; const cardConfigStruct = assign( baseLovelaceCardConfig, object({ area: optional(string()), + name: optional(string()), navigation_path: optional(string()), - theme: optional(string()), show_camera: optional(boolean()), + display_type: optional(enums(["compact", "icon", "picture", "camera"])), camera_view: optional(string()), - aspect_ratio: optional(string()), alert_classes: optional(array(string())), sensor_classes: optional(array(string())), + features: optional(array(any())), + features_position: optional(enums(["bottom", "inline"])), + aspect_ratio: optional(string()), }) ); @@ -52,6 +68,12 @@ export class HuiAreaCardEditor @state() private _numericDeviceClasses?: string[]; + private _featureContext = memoizeOne( + (areaId?: string): LovelaceCardFeatureContext => ({ + area_id: areaId, + }) + ); + private _schema = memoizeOne( ( localize: LocalizeFunc, @@ -61,103 +83,143 @@ export class HuiAreaCardEditor ) => [ { name: "area", selector: { area: {} } }, - { name: "show_camera", required: false, selector: { boolean: {} } }, - ...(showCamera - ? ([ - { - name: "camera_view", - selector: { - select: { - options: ["auto", "live"].map((value) => ({ - value, - label: localize( - `ui.panel.lovelace.editor.card.generic.camera_view_options.${value}` + { + name: "content", + flatten: true, + type: "expandable", + iconPath: mdiTextShort, + schema: [ + { + name: "", + type: "grid", + schema: [ + { name: "name", selector: { text: {} } }, + { + name: "display_type", + required: true, + selector: { + select: { + options: ["compact", "icon", "picture", "camera"].map( + (value) => ({ + value, + label: localize( + `ui.panel.lovelace.editor.card.area.display_type_options.${value}` + ), + }) ), - })), - mode: "dropdown", + mode: "dropdown", + }, }, }, + ], + }, + { + name: "", + type: "grid", + schema: [ + ...(showCamera + ? ([ + { + name: "camera_view", + selector: { + select: { + options: ["auto", "live"].map((value) => ({ + value, + label: localize( + `ui.panel.lovelace.editor.card.generic.camera_view_options.${value}` + ), + })), + mode: "dropdown", + }, + }, + }, + ] as const satisfies readonly HaFormSchema[]) + : []), + ], + }, + { + name: "alert_classes", + selector: { + select: { + reorder: true, + multiple: true, + custom_value: true, + options: binaryClasses, + }, }, - ] as const) - : []), + }, + { + name: "sensor_classes", + selector: { + select: { + reorder: true, + multiple: true, + custom_value: true, + options: sensorClasses, + }, + }, + }, + ], + }, { - name: "", - type: "grid", + name: "interactions", + type: "expandable", + flatten: true, + iconPath: mdiGestureTap, schema: [ { name: "navigation_path", required: false, selector: { navigation: {} }, }, - { name: "theme", required: false, selector: { theme: {} } }, - { - name: "aspect_ratio", - default: DEFAULT_ASPECT_RATIO, - selector: { text: {} }, - }, ], }, - { - name: "alert_classes", - selector: { - select: { - reorder: true, - multiple: true, - custom_value: true, - options: binaryClasses, - }, - }, - }, - { - name: "sensor_classes", - selector: { - select: { - reorder: true, - multiple: true, - custom_value: true, - options: sensorClasses, - }, - }, - }, - ] as const + ] as const satisfies readonly HaFormSchema[] ); - private _binaryClassesForArea = memoizeOne((area: string): string[] => - this._classesForArea(area, "binary_sensor") + private _binaryClassesForArea = memoizeOne( + (area: string | undefined): string[] => { + if (!area) { + return []; + } + + const binarySensorFilter = generateEntityFilter(this.hass!, { + domain: "binary_sensor", + area, + entity_category: "none", + }); + + const classes = Object.keys(this.hass!.entities) + .filter(binarySensorFilter) + .map((id) => this.hass!.states[id]?.attributes.device_class) + .filter((c): c is string => Boolean(c)); + + return [...new Set(classes)]; + } ); private _sensorClassesForArea = memoizeOne( - (area: string, numericDeviceClasses?: string[]): string[] => - this._classesForArea(area, "sensor", numericDeviceClasses) + (area: string | undefined, numericDeviceClasses?: string[]): string[] => { + if (!area) { + return []; + } + + const sensorFilter = generateEntityFilter(this.hass!, { + domain: "sensor", + area, + device_class: numericDeviceClasses, + entity_category: "none", + }); + + const classes = Object.keys(this.hass!.entities) + .filter(sensorFilter) + .map((id) => this.hass!.states[id]?.attributes.device_class) + .filter((c): c is string => Boolean(c)); + + return [...new Set(classes)]; + } ); - private _classesForArea( - area: string, - domain: "sensor" | "binary_sensor", - numericDeviceClasses?: string[] | undefined - ): string[] { - const entities = Object.values(this.hass!.entities).filter( - (e) => - computeDomain(e.entity_id) === domain && - !e.entity_category && - !e.hidden && - (e.area_id === area || - (e.device_id && this.hass!.devices[e.device_id]?.area_id === area)) - ); - - const classes = entities - .map((e) => this.hass!.states[e.entity_id]?.attributes.device_class || "") - .filter( - (c) => - c && - (domain !== "sensor" || - !numericDeviceClasses || - numericDeviceClasses.includes(c)) - ); - - return [...new Set(classes)]; - } - private _buildBinaryOptions = memoizeOne( (possibleClasses: string[], currentClasses: string[]): SelectOption[] => this._buildOptions("binary_sensor", possibleClasses, currentClasses) @@ -191,7 +253,14 @@ export class HuiAreaCardEditor public setConfig(config: AreaCardConfig): void { assert(config, cardConfigStruct); - this._config = config; + + const displayType = + config.display_type || (config.show_camera ? "camera" : "picture"); + this._config = { + ...config, + display_type: displayType, + }; + delete this._config.show_camera; } protected async updated() { @@ -202,16 +271,50 @@ export class HuiAreaCardEditor } } + private _featuresSchema = memoizeOne( + (localize: LocalizeFunc) => + [ + { + name: "features_position", + required: true, + selector: { + select: { + mode: "box", + options: ["bottom", "inline"].map((value) => ({ + label: localize( + `ui.panel.lovelace.editor.card.tile.features_position_options.${value}` + ), + description: localize( + `ui.panel.lovelace.editor.card.tile.features_position_options.${value}_description` + ), + value, + image: { + src: `/static/images/form/tile_features_position_${value}.svg`, + src_dark: `/static/images/form/tile_features_position_${value}_dark.svg`, + flip_rtl: true, + }, + })), + }, + }, + }, + ] as const satisfies readonly HaFormSchema[] + ); + + private _hasCompatibleFeatures = memoizeOne( + (context: LovelaceCardFeatureContext) => + getSupportedFeaturesType(this.hass!, context).length > 0 + ); + protected render() { if (!this.hass || !this._config) { return nothing; } - const possibleBinaryClasses = this._binaryClassesForArea( - this._config.area || "" - ); + const areaId = this._config!.area; + + const possibleBinaryClasses = this._binaryClassesForArea(this._config.area); const possibleSensorClasses = this._sensorClassesForArea( - this._config.area || "", + this._config.area, this._numericDeviceClasses ); const binarySelectOptions = this._buildBinaryOptions( @@ -223,68 +326,196 @@ export class HuiAreaCardEditor this._config.sensor_classes || DEVICE_CLASSES.sensor ); + const showCamera = this._config.display_type === "camera"; + + const displayType = + this._config.display_type || this._config.show_camera + ? "camera" + : "picture"; + const schema = this._schema( this.hass.localize, - this._config.show_camera || false, + showCamera, binarySelectOptions, sensorSelectOptions ); + const featuresSchema = this._featuresSchema(this.hass.localize); + const data = { camera_view: "auto", alert_classes: DEVICE_CLASSES.binary_sensor, sensor_classes: DEVICE_CLASSES.sensor, + features_position: "bottom", + display_type: displayType, ...this._config, }; + const featureContext = this._featureContext(areaId); + const hasCompatibleFeatures = this._hasCompatibleFeatures(featureContext); + return html` + + +

    + ${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.features" + )} +

    +
    + ${hasCompatibleFeatures + ? html` + + ` + : nothing} + +
    +
    `; } private _valueChanged(ev: CustomEvent): void { - const config = ev.detail.value; - if (!config.show_camera) { + const newConfig = ev.detail.value as AreaCardConfig; + + const config: AreaCardConfig = { + features: this._config!.features, + ...newConfig, + }; + + if (config.display_type !== "camera") { delete config.camera_view; } + fireEvent(this, "config-changed", { config }); } + private _featuresChanged(ev: CustomEvent) { + ev.stopPropagation(); + if (!this._config || !this.hass) { + return; + } + + const features = ev.detail.features as LovelaceCardFeatureConfig[]; + const config: AreaCardConfig = { + ...this._config, + features, + }; + + if (features.length === 0) { + delete config.features; + } + + fireEvent(this, "config-changed", { config }); + } + + private _editDetailElement(ev: HASSDomEvent): void { + const index = ev.detail.subElementConfig.index; + const config = this._config!.features![index!]; + const featureContext = this._featureContext(this._config!.area); + + fireEvent(this, "edit-sub-element", { + config: config, + saveConfig: (newConfig) => this._updateFeature(index!, newConfig), + context: featureContext, + type: "feature", + } as EditSubElementEvent< + LovelaceCardFeatureConfig, + LovelaceCardFeatureContext + >); + } + + private _updateFeature(index: number, feature: LovelaceCardFeatureConfig) { + const features = this._config!.features!.concat(); + features[index] = feature; + const config = { ...this._config!, features }; + fireEvent(this, "config-changed", { + config: config, + }); + } + + private _computeHelperCallback = ( + schema: + | SchemaUnion> + | SchemaUnion> + ): string | undefined => { + switch (schema.name) { + case "alert_classes": + if (this._config?.display_type === "compact") { + return this.hass!.localize( + `ui.panel.lovelace.editor.card.area.alert_classes_helper` + ); + } + return undefined; + default: + return undefined; + } + }; + private _computeLabelCallback = ( - schema: SchemaUnion> + schema: + | SchemaUnion> + | SchemaUnion> ) => { switch (schema.name) { - case "theme": - return `${this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.theme" - )} (${this.hass!.localize( - "ui.panel.lovelace.editor.card.config.optional" - )})`; case "area": return this.hass!.localize("ui.panel.lovelace.editor.card.area.name"); + + case "name": + case "camera_view": + case "content": + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); case "navigation_path": return this.hass!.localize( "ui.panel.lovelace.editor.action-editor.navigation_path" ); - case "aspect_ratio": + case "interactions": + case "features_position": return this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.aspect_ratio" - ); - case "camera_view": - return this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.camera_view" + `ui.panel.lovelace.editor.card.tile.${schema.name}` ); } return this.hass!.localize( `ui.panel.lovelace.editor.card.area.${schema.name}` ); }; + + static get styles() { + return [ + configElementStyle, + css` + ha-form { + display: block; + margin-bottom: 24px; + } + .features-form { + margin-bottom: 8px; + } + `, + ]; + } } declare global { diff --git a/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts new file mode 100644 index 0000000000..ac79793497 --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts @@ -0,0 +1,180 @@ +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import type { LocalizeFunc } from "../../../../common/translations/localize"; +import "../../../../components/ha-form/ha-form"; +import type { + HaFormSchema, + SchemaUnion, +} from "../../../../components/ha-form/types"; +import type { HomeAssistant } from "../../../../types"; +import { getAreaControlEntities } from "../../card-features/hui-area-controls-card-feature"; +import { + AREA_CONTROLS, + type AreaControl, + type AreaControlsCardFeatureConfig, + type LovelaceCardFeatureContext, +} from "../../card-features/types"; +import type { LovelaceCardFeatureEditor } from "../../types"; + +type AreaControlsCardFeatureData = AreaControlsCardFeatureConfig & { + customize_controls: boolean; +}; + +@customElement("hui-area-controls-card-feature-editor") +export class HuiAreaControlsCardFeatureEditor + extends LitElement + implements LovelaceCardFeatureEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public context?: LovelaceCardFeatureContext; + + @state() private _config?: AreaControlsCardFeatureConfig; + + public setConfig(config: AreaControlsCardFeatureConfig): void { + this._config = config; + } + + private _schema = memoizeOne( + ( + localize: LocalizeFunc, + customizeControls: boolean, + compatibleControls: AreaControl[] + ) => + [ + { + name: "customize_controls", + selector: { + boolean: {}, + }, + }, + ...(customizeControls + ? ([ + { + name: "controls", + selector: { + select: { + reorder: true, + multiple: true, + options: compatibleControls.map((control) => ({ + value: control, + label: localize( + `ui.panel.lovelace.editor.features.types.area-controls.controls_options.${control}` + ), + })), + }, + }, + }, + ] as const satisfies readonly HaFormSchema[]) + : []), + ] as const satisfies readonly HaFormSchema[] + ); + + private _compatibleControls = memoizeOne( + ( + areaId: string, + // needed to update memoized function when entities, devices or areas change + _entities: HomeAssistant["entities"], + _devices: HomeAssistant["devices"], + _areas: HomeAssistant["areas"] + ) => { + if (!this.hass) { + return []; + } + const controlEntities = getAreaControlEntities( + AREA_CONTROLS as unknown as AreaControl[], + areaId, + this.hass! + ); + return ( + Object.keys(controlEntities) as (keyof typeof controlEntities)[] + ).filter((control) => controlEntities[control].length > 0); + } + ); + + protected render() { + if (!this.hass || !this._config || !this.context?.area_id) { + return nothing; + } + + const compatibleControls = this._compatibleControls( + this.context.area_id, + this.hass.entities, + this.hass.devices, + this.hass.areas + ); + + if (compatibleControls.length === 0) { + return html` + + ${this.hass.localize( + "ui.panel.lovelace.editor.features.types.area-controls.no_compatible_controls" + )} + + `; + } + + const data: AreaControlsCardFeatureData = { + ...this._config, + customize_controls: this._config.controls !== undefined, + }; + + const schema = this._schema( + this.hass.localize, + data.customize_controls, + compatibleControls + ); + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + const { customize_controls, ...config } = ev.detail + .value as AreaControlsCardFeatureData; + + if (customize_controls && !config.controls) { + config.controls = this._compatibleControls( + this.context!.area_id!, + this.hass!.entities, + this.hass!.devices, + this.hass!.areas + ).concat(); + } + + if (!customize_controls && config.controls) { + delete config.controls; + } + + fireEvent(this, "config-changed", { config: config }); + } + + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "controls": + case "customize_controls": + return this.hass!.localize( + `ui.panel.lovelace.editor.features.types.area-controls.${schema.name}` + ); + default: + return ""; + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-area-controls-card-feature-editor": HuiAreaControlsCardFeatureEditor; + } +} diff --git a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts index b92723ecc2..befbb73c7b 100644 --- a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts @@ -18,6 +18,7 @@ import { } from "../../../../data/lovelace_custom_cards"; import type { HomeAssistant } from "../../../../types"; import { supportsAlarmModesCardFeature } from "../../card-features/hui-alarm-modes-card-feature"; +import { supportsAreaControlsCardFeature } from "../../card-features/hui-area-controls-card-feature"; import { supportsClimateFanModesCardFeature } from "../../card-features/hui-climate-fan-modes-card-feature"; import { supportsClimateHvacModesCardFeature } from "../../card-features/hui-climate-hvac-modes-card-feature"; import { supportsClimatePresetModesCardFeature } from "../../card-features/hui-climate-preset-modes-card-feature"; @@ -61,6 +62,7 @@ type SupportsFeature = ( const UI_FEATURE_TYPES = [ "alarm-modes", + "area-controls", "climate-fan-modes", "climate-hvac-modes", "climate-preset-modes", @@ -95,6 +97,7 @@ type UiFeatureTypes = (typeof UI_FEATURE_TYPES)[number]; const EDITABLES_FEATURE_TYPES = new Set([ "alarm-modes", + "area-controls", "climate-fan-modes", "climate-hvac-modes", "climate-preset-modes", @@ -116,6 +119,7 @@ const SUPPORTS_FEATURE_TYPES: Record< SupportsFeature | undefined > = { "alarm-modes": supportsAlarmModesCardFeature, + "area-controls": supportsAreaControlsCardFeature, "climate-fan-modes": supportsClimateFanModesCardFeature, "climate-swing-modes": supportsClimateSwingModesCardFeature, "climate-swing-horizontal-modes": diff --git a/src/translations/en.json b/src/translations/en.json index d64c5f2ce5..895ac9fbc8 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -7172,10 +7172,17 @@ }, "area": { "name": "Area", - "alert_classes": "Alert Classes", - "sensor_classes": "Sensor Classes", + "alert_classes": "Alert classes", + "alert_classes_helper": "In compact style, only the first one will be shown. Order alerts by priority.", + "sensor_classes": "Sensor classes", "description": "The Area card automatically displays entities of a specific area.", - "show_camera": "Show camera feed instead of area picture" + "display_type": "Display type", + "display_type_options": { + "compact": "Compact", + "icon": "Area icon", + "picture": "Area picture", + "camera": "Camera feed" + } }, "calendar": { "name": "Calendar", @@ -7832,6 +7839,17 @@ "ask": "Ask" }, "backup_not_supported": "Backup is not supported." + }, + "area-controls": { + "label": "Area controls", + "customize_controls": "Customize controls", + "controls": "Controls", + "controls_options": { + "light": "Lights", + "fan": "Fans", + "switch": "Switches" + }, + "no_compatible_controls": "No compatible controls available for this area" } } }, From b608bd949b55c74d9c89266f215ea15929b573ba Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 20 Jun 2025 15:48:59 +0200 Subject: [PATCH 068/216] Add `fields` and `multiple` support to object selector (#25843) * Add schema and multiple for object selector * Add selector to gallery * Fix description * Improve formatting * Add ellipsis * Update fields instead of schema * Update gallery * Update format * Fix format value * Fix dialog size --- gallery/src/pages/components/ha-selector.ts | 28 ++ .../ha-selector/ha-selector-object.ts | 276 +++++++++++++++++- src/data/selector.ts | 14 +- src/data/selector/format_selector_value.ts | 104 +++++++ src/dialogs/form/dialog-form.ts | 89 ++++++ src/dialogs/form/show-form-dialog.ts | 45 +++ 6 files changed, 550 insertions(+), 6 deletions(-) create mode 100644 src/data/selector/format_selector_value.ts create mode 100644 src/dialogs/form/dialog-form.ts create mode 100644 src/dialogs/form/show-form-dialog.ts diff --git a/gallery/src/pages/components/ha-selector.ts b/gallery/src/pages/components/ha-selector.ts index 0f50f6421f..4420b483ed 100644 --- a/gallery/src/pages/components/ha-selector.ts +++ b/gallery/src/pages/components/ha-selector.ts @@ -416,6 +416,34 @@ const SCHEMAS: { }, }, }, + items: { + name: "Items", + selector: { + object: { + label_field: "name", + description_field: "value", + multiple: true, + fields: { + name: { + label: "Name", + selector: { text: {} }, + required: true, + }, + value: { + label: "Value", + selector: { + number: { + mode: "slider", + min: 0, + max: 100, + unit_of_measurement: "%", + }, + }, + }, + }, + }, + }, + }, }, }, ]; diff --git a/src/components/ha-selector/ha-selector-object.ts b/src/components/ha-selector/ha-selector-object.ts index f2b9040b20..bca45148b3 100644 --- a/src/components/ha-selector/ha-selector-object.ts +++ b/src/components/ha-selector/ha-selector-object.ts @@ -1,16 +1,27 @@ -import type { PropertyValues } from "lit"; -import { html, LitElement } from "lit"; +import { mdiClose, mdiDelete, mdiDrag, mdiPencil } from "@mdi/js"; +import { css, html, LitElement, nothing, type PropertyValues } from "lit"; import { customElement, property, query } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { ensureArray } from "../../common/array/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; +import type { ObjectSelector } from "../../data/selector"; +import { formatSelectorValue } from "../../data/selector/format_selector_value"; +import { showFormDialog } from "../../dialogs/form/show-form-dialog"; import type { HomeAssistant } from "../../types"; -import "../ha-yaml-editor"; +import type { HaFormSchema } from "../ha-form/types"; import "../ha-input-helper-text"; +import "../ha-md-list"; +import "../ha-md-list-item"; +import "../ha-sortable"; +import "../ha-yaml-editor"; import type { HaYamlEditor } from "../ha-yaml-editor"; @customElement("ha-selector-object") export class HaObjectSelector extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public selector!: ObjectSelector; + @property() public value?: any; @property() public label?: string; @@ -23,11 +34,136 @@ export class HaObjectSelector extends LitElement { @property({ type: Boolean }) public required = true; - @query("ha-yaml-editor", true) private _yamlEditor!: HaYamlEditor; + @property({ attribute: false }) public localizeValue?: ( + key: string + ) => string; + + @query("ha-yaml-editor", true) private _yamlEditor?: HaYamlEditor; private _valueChangedFromChild = false; + private _computeLabel = (schema: HaFormSchema): string => { + const translationKey = this.selector.object?.translation_key; + + if (this.localizeValue && translationKey) { + const label = this.localizeValue( + `${translationKey}.fields.${schema.name}` + ); + if (label) { + return label; + } + } + return this.selector.object?.fields?.[schema.name]?.label || schema.name; + }; + + private _renderItem(item: any, index: number) { + const labelField = + this.selector.object!.label_field || + Object.keys(this.selector.object!.fields!)[0]; + + const labelSelector = this.selector.object!.fields![labelField].selector; + + const label = labelSelector + ? formatSelectorValue(this.hass, item[labelField], labelSelector) + : ""; + + let description = ""; + + const descriptionField = this.selector.object!.description_field; + if (descriptionField) { + const descriptionSelector = + this.selector.object!.fields![descriptionField].selector; + + description = descriptionSelector + ? formatSelectorValue( + this.hass, + item[descriptionField], + descriptionSelector + ) + : ""; + } + + const reorderable = this.selector.object!.multiple || false; + const multiple = this.selector.object!.multiple || false; + return html` + + ${reorderable + ? html` + + ` + : nothing} +
    ${label}
    + ${description + ? html`
    + ${description} +
    ` + : nothing} + + +
    + `; + } + protected render() { + if (!this.selector.object) { + return nothing; + } + + if (this.selector.object.fields) { + if (this.selector.object.multiple) { + const items = ensureArray(this.value ?? []); + return html` + ${this.label ? html`` : nothing} +
    + + + ${items.map((item, index) => this._renderItem(item, index))} + + + + ${this.hass.localize("ui.common.add")} + +
    + `; + } + + return html` + ${this.label ? html`` : nothing} +
    + ${this.value + ? html` + ${this._renderItem(this.value, 0)} + ` + : html` + + ${this.hass.localize("ui.common.add")} + + `} +
    + `; + } + return html` { + if (!selector.object || !selector.object.fields) { + return []; + } + return Object.entries(selector.object.fields).map(([key, field]) => ({ + name: key, + selector: field.selector, + required: field.required ?? false, + })); + }); + + private _itemMoved(ev) { + ev.stopPropagation(); + const newIndex = ev.detail.newIndex; + const oldIndex = ev.detail.oldIndex; + if (!this.selector.object!.multiple) { + return; + } + const newValue = ensureArray(this.value ?? []).concat(); + const item = newValue.splice(oldIndex, 1)[0]; + newValue.splice(newIndex, 0, item); + fireEvent(this, "value-changed", { value: newValue }); + } + + private async _addItem(ev) { + ev.stopPropagation(); + + const newItem = await showFormDialog(this, { + title: this.hass.localize("ui.common.add"), + schema: this._schema(this.selector), + data: {}, + computeLabel: this._computeLabel, + submitText: this.hass.localize("ui.common.add"), + }); + + if (newItem === null) { + return; + } + + if (!this.selector.object!.multiple) { + fireEvent(this, "value-changed", { value: newItem }); + return; + } + + const newValue = ensureArray(this.value ?? []).concat(); + newValue.push(newItem); + fireEvent(this, "value-changed", { value: newValue }); + } + + private async _editItem(ev) { + ev.stopPropagation(); + const item = ev.currentTarget.item; + const index = ev.currentTarget.index; + + const updatedItem = await showFormDialog(this, { + title: this.hass.localize("ui.common.edit"), + schema: this._schema(this.selector), + data: item, + computeLabel: this._computeLabel, + submitText: this.hass.localize("ui.common.save"), + }); + + if (updatedItem === null) { + return; + } + + if (!this.selector.object!.multiple) { + fireEvent(this, "value-changed", { value: updatedItem }); + return; + } + + const newValue = ensureArray(this.value ?? []).concat(); + newValue[index] = updatedItem; + fireEvent(this, "value-changed", { value: newValue }); + } + + private _deleteItem(ev) { + ev.stopPropagation(); + const index = ev.currentTarget.index; + + if (!this.selector.object!.multiple) { + fireEvent(this, "value-changed", { value: undefined }); + return; + } + + const newValue = ensureArray(this.value ?? []).concat(); + newValue.splice(index, 1); + fireEvent(this, "value-changed", { value: newValue }); + } + protected updated(changedProps: PropertyValues) { super.updated(changedProps); - if (changedProps.has("value") && !this._valueChangedFromChild) { + if ( + changedProps.has("value") && + !this._valueChangedFromChild && + this._yamlEditor + ) { this._yamlEditor.setValue(this.value); } this._valueChangedFromChild = false; @@ -63,6 +293,42 @@ export class HaObjectSelector extends LitElement { } fireEvent(this, "value-changed", { value }); } + + static get styles() { + return [ + css` + ha-md-list { + gap: 8px; + } + ha-md-list-item { + border: 1px solid var(--divider-color); + border-radius: 8px; + --ha-md-list-item-gap: 0; + --md-list-item-top-space: 0; + --md-list-item-bottom-space: 0; + --md-list-item-leading-space: 12px; + --md-list-item-trailing-space: 4px; + --md-list-item-two-line-container-height: 48px; + --md-list-item-one-line-container-height: 48px; + } + .handle { + cursor: move; + padding: 8px; + margin-inline-start: -8px; + } + label { + margin-bottom: 8px; + display: block; + } + ha-md-list-item .label, + ha-md-list-item .description { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + `, + ]; + } } declare global { diff --git a/src/data/selector.ts b/src/data/selector.ts index 60d2b133fe..c76a458a05 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -334,8 +334,20 @@ export interface NumberSelector { } | null; } +interface ObjectSelectorField { + selector: Selector; + label?: string; + required?: boolean; +} + export interface ObjectSelector { - object: {} | null; + object?: { + label_field?: string; + description_field?: string; + translation_key?: string; + fields?: Record; + multiple?: boolean; + } | null; } export interface AssistPipelineSelector { diff --git a/src/data/selector/format_selector_value.ts b/src/data/selector/format_selector_value.ts new file mode 100644 index 0000000000..47950879d3 --- /dev/null +++ b/src/data/selector/format_selector_value.ts @@ -0,0 +1,104 @@ +import { ensureArray } from "../../common/array/ensure-array"; +import { computeAreaName } from "../../common/entity/compute_area_name"; +import { computeDeviceName } from "../../common/entity/compute_device_name"; +import { computeEntityName } from "../../common/entity/compute_entity_name"; +import { getEntityContext } from "../../common/entity/context/get_entity_context"; +import { blankBeforeUnit } from "../../common/translations/blank_before_unit"; +import type { HomeAssistant } from "../../types"; +import type { Selector } from "../selector"; + +export const formatSelectorValue = ( + hass: HomeAssistant, + value: any, + selector?: Selector +) => { + if (value == null) { + return ""; + } + + if (!selector) { + return ensureArray(value).join(", "); + } + + if ("text" in selector) { + const { prefix, suffix } = selector.text || {}; + + const texts = ensureArray(value); + return texts + .map((text) => `${prefix || ""}${text}${suffix || ""}`) + .join(", "); + } + + if ("number" in selector) { + const { unit_of_measurement } = selector.number || {}; + const numbers = ensureArray(value); + return numbers + .map((number) => { + const num = Number(number); + if (isNaN(num)) { + return number; + } + return unit_of_measurement + ? `${num}${blankBeforeUnit(unit_of_measurement, hass.locale)}${unit_of_measurement}` + : num.toString(); + }) + .join(", "); + } + + if ("floor" in selector) { + const floors = ensureArray(value); + return floors + .map((floorId) => { + const floor = hass.floors[floorId]; + if (!floor) { + return floorId; + } + return floor.name || floorId; + }) + .join(", "); + } + + if ("area" in selector) { + const areas = ensureArray(value); + return areas + .map((areaId) => { + const area = hass.areas[areaId]; + if (!area) { + return areaId; + } + return computeAreaName(area); + }) + .join(", "); + } + + if ("entity" in selector) { + const entities = ensureArray(value); + return entities + .map((entityId) => { + const stateObj = hass.states[entityId]; + if (!stateObj) { + return entityId; + } + const { device } = getEntityContext(stateObj, hass); + const deviceName = device ? computeDeviceName(device) : undefined; + const entityName = computeEntityName(stateObj, hass); + return [deviceName, entityName].filter(Boolean).join(" ") || entityId; + }) + .join(", "); + } + + if ("device" in selector) { + const devices = ensureArray(value); + return devices + .map((deviceId) => { + const device = hass.devices[deviceId]; + if (!device) { + return deviceId; + } + return device.name || deviceId; + }) + .join(", "); + } + + return ensureArray(value).join(", "); +}; diff --git a/src/dialogs/form/dialog-form.ts b/src/dialogs/form/dialog-form.ts new file mode 100644 index 0000000000..af755a2224 --- /dev/null +++ b/src/dialogs/form/dialog-form.ts @@ -0,0 +1,89 @@ +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/ha-button"; +import { createCloseHeading } from "../../components/ha-dialog"; +import "../../components/ha-form/ha-form"; +import type { HomeAssistant } from "../../types"; +import type { HassDialog } from "../make-dialog-manager"; +import type { FormDialogData, FormDialogParams } from "./show-form-dialog"; +import { haStyleDialog } from "../../resources/styles"; + +@customElement("dialog-form") +export class DialogForm + extends LitElement + implements HassDialog +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @state() private _params?: FormDialogParams; + + @state() private _data: FormDialogData = {}; + + public async showDialog(params: FormDialogParams): Promise { + this._params = params; + this._data = params.data || {}; + } + + public closeDialog() { + this._params = undefined; + this._data = {}; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + return true; + } + + private _submit(): void { + this._params?.submit?.(this._data); + this.closeDialog(); + } + + private _cancel(): void { + this._params?.cancel?.(); + this.closeDialog(); + } + + private _valueChanged(ev: CustomEvent): void { + this._data = ev.detail.value; + } + + protected render() { + if (!this._params || !this.hass) { + return nothing; + } + + return html` + + + + + ${this._params.cancelText || this.hass.localize("ui.common.cancel")} + + + ${this._params.submitText || this.hass.localize("ui.common.save")} + + + `; + } + + static styles = [haStyleDialog, css``]; +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-form": DialogForm; + } +} diff --git a/src/dialogs/form/show-form-dialog.ts b/src/dialogs/form/show-form-dialog.ts new file mode 100644 index 0000000000..3cd49fd6ce --- /dev/null +++ b/src/dialogs/form/show-form-dialog.ts @@ -0,0 +1,45 @@ +import { fireEvent } from "../../common/dom/fire_event"; +import type { HaFormSchema } from "../../components/ha-form/types"; + +export type FormDialogData = Record; + +export interface FormDialogParams { + title: string; + schema: HaFormSchema[]; + data?: FormDialogData; + submit?: (data?: FormDialogData) => void; + cancel?: () => void; + computeLabel?: (schema, data) => string | undefined; + computeHelper?: (schema) => string | undefined; + submitText?: string; + cancelText?: string; +} + +export const showFormDialog = ( + element: HTMLElement, + dialogParams: FormDialogParams +) => + new Promise((resolve) => { + const origCancel = dialogParams.cancel; + const origSubmit = dialogParams.submit; + + fireEvent(element, "show-dialog", { + dialogTag: "dialog-form", + dialogImport: () => import("./dialog-form"), + dialogParams: { + ...dialogParams, + cancel: () => { + resolve(null); + if (origCancel) { + origCancel(); + } + }, + submit: (data: FormDialogData) => { + resolve(data); + if (origSubmit) { + origSubmit(data); + } + }, + }, + }); + }); From 52a02093e395c4a9755901dbe4f3b2b825260361 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 20 Jun 2025 12:07:29 -0400 Subject: [PATCH 069/216] Allow changing LLM Task preferences (#25779) * Allow changing LLM Task preferences * value-changed --- src/data/ai_task.ts | 43 +++++ src/data/icons.ts | 2 + .../config/voice-assistants/ai-task-pref.ts | 159 ++++++++++++++++++ .../ha-config-voice-assistants-assistants.ts | 7 + src/translations/en.json | 6 + src/types.ts | 8 +- 6 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 src/data/ai_task.ts create mode 100644 src/panels/config/voice-assistants/ai-task-pref.ts diff --git a/src/data/ai_task.ts b/src/data/ai_task.ts new file mode 100644 index 0000000000..c4f42d0bc8 --- /dev/null +++ b/src/data/ai_task.ts @@ -0,0 +1,43 @@ +import type { HomeAssistant } from "../types"; + +export interface AITaskPreferences { + gen_text_entity_id: string | null; +} + +export interface GenTextTaskResult { + conversation_id: string; + text: string; +} + +export const fetchAITaskPreferences = (hass: HomeAssistant) => + hass.callWS({ + type: "ai_task/preferences/get", + }); + +export const saveAITaskPreferences = ( + hass: HomeAssistant, + preferences: Partial +) => + hass.callWS({ + type: "ai_task/preferences/set", + ...preferences, + }); + +export const generateTextAITask = async ( + hass: HomeAssistant, + task: { + task_name: string; + entity_id?: string; + instructions: string; + } +): Promise => { + const result = await hass.callService( + "ai_task", + "generate_text", + task, + undefined, + true, + true + ); + return result.response!; +}; diff --git a/src/data/icons.ts b/src/data/icons.ts index 93cd21fee1..5d870aac2e 100644 --- a/src/data/icons.ts +++ b/src/data/icons.ts @@ -37,6 +37,7 @@ import { mdiRoomService, mdiScriptText, mdiSpeakerMessage, + mdiStarFourPoints, mdiThermostat, mdiTimerOutline, mdiToggleSwitch, @@ -66,6 +67,7 @@ export const DEFAULT_DOMAIN_ICON = mdiBookmark; /** Fallback icons for each domain */ export const FALLBACK_DOMAIN_ICONS = { + ai_task: mdiStarFourPoints, air_quality: mdiAirFilter, alert: mdiAlert, automation: mdiRobot, diff --git a/src/panels/config/voice-assistants/ai-task-pref.ts b/src/panels/config/voice-assistants/ai-task-pref.ts new file mode 100644 index 0000000000..9eba296781 --- /dev/null +++ b/src/panels/config/voice-assistants/ai-task-pref.ts @@ -0,0 +1,159 @@ +import "@material/mwc-button"; +import { mdiHelpCircle, mdiStarFourPoints } from "@mdi/js"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import "../../../components/ha-card"; +import "../../../components/ha-settings-row"; +import "../../../components/entity/ha-entity-picker"; +import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker"; +import type { HomeAssistant } from "../../../types"; +import { brandsUrl } from "../../../util/brands-url"; +import { + fetchAITaskPreferences, + saveAITaskPreferences, + type AITaskPreferences, +} from "../../../data/ai_task"; +import { documentationUrl } from "../../../util/documentation-url"; + +@customElement("ai-task-pref") +export class AITaskPref extends LitElement { + @property({ type: Boolean, reflect: true }) public narrow = false; + + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _prefs?: AITaskPreferences; + + protected firstUpdated(changedProps) { + super.firstUpdated(changedProps); + fetchAITaskPreferences(this.hass).then((prefs) => { + this._prefs = prefs; + }); + } + + protected render() { + if (!this._prefs) { + return nothing; + } + + return html` + +

    + ${this.hass.localize( + "ui.panel.config.voice_assistants.ai_task.header" + )} +

    +
    + + + +
    +
    +

    + ${this.hass!.localize( + "ui.panel.config.voice_assistants.ai_task.description", + { + button: html``, + } + )} +

    + + + ${this.hass!.localize( + "ui.panel.config.voice_assistants.ai_task.gen_text_header" + )} + + + ${this.hass!.localize( + "ui.panel.config.voice_assistants.ai_task.gen_text_description" + )} + + + +
    +
    + `; + } + + private async _handlePrefChange( + ev: CustomEvent<{ value: string | undefined }> + ) { + const input = ev.target as HaEntityPicker; + const key = input.getAttribute("data-name") as keyof AITaskPreferences; + const entityId = ev.detail.value || null; + const oldPrefs = this._prefs; + this._prefs = { ...this._prefs!, [key]: entityId }; + try { + this._prefs = await saveAITaskPreferences(this.hass, { + [key]: entityId, + }); + } catch (_err: any) { + this._prefs = oldPrefs; + } + } + + static styles = css` + a { + color: var(--primary-color); + } + ha-settings-row { + padding: 0; + } + .header-actions { + position: absolute; + right: 0px; + inset-inline-end: 0px; + inset-inline-start: initial; + top: 24px; + display: flex; + flex-direction: row; + } + .header-actions .icon-link { + margin-top: -16px; + margin-right: 8px; + margin-inline-end: 8px; + margin-inline-start: initial; + direction: var(--direction); + color: var(--secondary-text-color); + } + ha-entity-picker { + flex: 1; + margin-left: 16px; + } + :host([narrow]) ha-entity-picker { + margin-left: 0; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ai-task-pref": AITaskPref; + } +} diff --git a/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts b/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts index ff959d7830..f9aa48319b 100644 --- a/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts +++ b/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts @@ -8,6 +8,7 @@ import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-tabs-subpage"; import type { HomeAssistant, Route } from "../../../types"; import "./assist-pref"; +import "./ai-task-pref"; import "./cloud-alexa-pref"; import "./cloud-discover"; import "./cloud-google-pref"; @@ -53,6 +54,12 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement { > ` : nothing} + ${isComponentLoaded(this.hass, "ai_task") + ? html`` + : nothing} ${this.cloudStatus?.logged_in ? html` { context: Context; - response?: any; + response?: T; } export interface ServiceCallRequest { @@ -248,14 +248,14 @@ export interface HomeAssistant { user?: CurrentUser; userData?: CoreFrontendUserData | null; hassUrl(path?): string; - callService( + callService( domain: ServiceCallRequest["domain"], service: ServiceCallRequest["service"], serviceData?: ServiceCallRequest["serviceData"], target?: ServiceCallRequest["target"], notifyOnError?: boolean, returnResponse?: boolean - ): Promise; + ): Promise>; callApi( method: "GET" | "POST" | "PUT" | "DELETE", path: string, From 1059b519afbb2040560ce52e88cca1dff2b1135b Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 20 Jun 2025 12:07:47 -0400 Subject: [PATCH 070/216] Add LLM Task to suggest automation name (#25778) * Add AI Task to suggest automation name * Use state and update styling --- .../dialog-automation-save.ts | 56 ++++++++++++++++++- src/translations/en.json | 3 +- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts index 4df25b260c..99fd37e5ff 100644 --- a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts +++ b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts @@ -1,8 +1,9 @@ import "@material/mwc-button"; -import type { CSSResultGroup } from "lit"; +import type { CSSResultGroup, PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { mdiClose, mdiPlus } from "@mdi/js"; +import { mdiClose, mdiPlus, mdiStarFourPoints } from "@mdi/js"; +import { dump } from "js-yaml"; import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-alert"; import "../../../../components/ha-domain-icon"; @@ -24,6 +25,11 @@ import type { SaveDialogParams, } from "./show-dialog-automation-save"; import { supportsMarkdownHelper } from "../../../../common/translations/markdown_support"; +import { + fetchAITaskPreferences, + generateTextAITask, +} from "../../../../data/ai_task"; +import { isComponentLoaded } from "../../../../common/config/is_component_loaded"; @customElement("ha-dialog-automation-save") class DialogAutomationSave extends LitElement implements HassDialog { @@ -37,9 +43,11 @@ class DialogAutomationSave extends LitElement implements HassDialog { @state() private _entryUpdates!: EntityRegistryUpdate; + @state() private _canSuggest = false; + private _params!: SaveDialogParams; - private _newName?: string; + @state() private _newName?: string; private _newIcon?: string; @@ -81,6 +89,15 @@ class DialogAutomationSave extends LitElement implements HassDialog { return true; } + protected firstUpdated(changedProperties: PropertyValues): void { + super.firstUpdated(changedProperties); + if (isComponentLoaded(this.hass, "ai_task")) { + fetchAITaskPreferences(this.hass).then((prefs) => { + this._canSuggest = prefs.gen_text_entity_id !== null; + }); + } + } + protected _renderOptionalChip(id: string, label: string) { if (this._visibleOptionals.includes(id)) { return nothing; @@ -250,6 +267,21 @@ class DialogAutomationSave extends LitElement implements HassDialog { .path=${mdiClose} > ${this._params.title || title} + ${this._canSuggest + ? html` + + + + ` + : nothing} ${this._error ? html` { if (!this._newName) { this._error = "Name is required"; @@ -381,6 +427,10 @@ class DialogAutomationSave extends LitElement implements HassDialog { .destructive { --mdc-theme-primary: var(--error-color); } + + #suggest { + margin: 8px 16px; + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index 9c4f45b55e..9b450560a1 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -380,7 +380,8 @@ "replace": "Replace", "append": "Append", "supports_markdown": "Supports {markdown_help_link}", - "markdown": "Markdown" + "markdown": "Markdown", + "suggest_ai": "Suggest with AI" }, "components": { "selectors": { From 9c8d6a939b056e3dceac3ccd52bf96bea9cc2fd0 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Fri, 20 Jun 2025 20:20:00 +0200 Subject: [PATCH 071/216] Fix spelling of "to log in", "to set up" and more (#25855) - add missing space to "log in" where it's the verb - add missing space to "set up" where it's the verb - fix sentence-casing of "Create area" - fix spelling of "ID" as abbreviation --- src/translations/en.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 9b450560a1..828f4ad0ac 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -953,7 +953,7 @@ "documentation": "documentation", "no_local_media_found": "No local media found", "no_media_folder": "It looks like you have not yet created a media directory.", - "setup_local_help": "Check the {documentation} on how to setup local media.", + "setup_local_help": "Check the {documentation} on how to set up local media.", "file_management": { "title": "Media management", "manage": "Manage", @@ -2278,7 +2278,7 @@ "integrations_page": "Integrations page", "no_areas": "Looks like you have no areas yet!", "unassigned_areas": "Unassigned areas", - "create_area": "Create Area", + "create_area": "Create area", "create_floor": "Create floor", "floor": { "edit_floor": "Edit floor", @@ -3888,7 +3888,7 @@ }, "persistent_notification": { "label": "Persistent notification", - "notification_id": "Notification Id", + "notification_id": "Notification ID", "update_type": "Update type", "update_types": { "added": "added", @@ -5196,7 +5196,7 @@ "create": "Create", "update": "Update", "confirm_delete_user_title": "Delete user account", - "confirm_delete_user_text": "The user account for ''{name}'' will be permanently deleted. You can still track the user, but the person will no longer be able to login.", + "confirm_delete_user_text": "The user account for ''{name}'' will be permanently deleted. You can still track the user, but the person will no longer be able to log in.", "allow_login": "Allow login", "allow_login_description": "Allow access using username and password.", "username": "[%key:ui::panel::config::users::editor::username%]", @@ -5245,7 +5245,7 @@ "discovered": "Discovered", "disabled": "Disabled", "available_integrations": "Available integrations", - "new_flow": "Setup another instance of {integration}", + "new_flow": "Set up another instance of {integration}", "attention": "Attention required", "configured": "Configured", "new": "Select brand", @@ -5300,7 +5300,7 @@ "entries_system": "[%key:ui::panel::config::integrations::integration_page::entries%]", "entries_entity": "[%key:ui::panel::config::integrations::integration_page::entries%]", "no_entries": "No entries", - "yaml_entry": "This integration was not setup via the UI, you have either set it up in YAML or it is a dependency set up by another integration. If you want to configure it, you will need to do so in your configuration.yaml file.", + "yaml_entry": "This integration was not set up via the UI, you have either set it up in YAML or it is a dependency set up by another integration. If you want to configure it, you will need to do so in your configuration.yaml file.", "attention_entries": "Needs attention", "add_entry": "Add entry", "add_device": "Add device", @@ -5373,7 +5373,7 @@ "legacy_integration": "Legacy integration", "custom_overwrites_core": "Custom integration that replaces a core component", "depends_on_cloud": "Requires Internet", - "yaml_only": "This integration cannot be setup from the UI", + "yaml_only": "This integration cannot be set up from the UI", "no_config_flow": "This integration was not set up from the UI", "disabled_polling": "Automatic polling for updated data disabled", "debug_logging_enabled": "Debug logging enabled", @@ -5424,7 +5424,7 @@ "could_not_load": "Config flow could not be loaded", "not_loaded": "The integration could not be loaded, try to restart Home Assistant.", "supported_brand_flow": "Support for {supported_brand} devices is provided by {flow_domain_name}. Do you want to continue?", - "missing_zwave_zigbee_title": "{integration} is not setup", + "missing_zwave_zigbee_title": "{integration} is not set up", "missing_zwave_zigbee": "To add a {brand} device, you first need {supported_hardware_link} and the {integration} integration set up. If you already have the hardware then you can proceed with the setup of {integration}.", "missing_matter": "To add a {brand} device, you first need the {integration} integration and {supported_hardware_link}. Do you want to proceed with the setup of {integration}?", "supported_hardware": "supported hardware", @@ -5470,7 +5470,7 @@ "admin_description": "Administrators can manage users, devices, automations and dashboards.", "group": "Group", "active": "Active", - "active_description": "Controls if user can login", + "active_description": "Controls if user can log in", "local_access_only": "Local access only", "local_access_only_description": "Can only log in from the local network", "system_generated": "System user", From 8324c236182679d0f3a782b07e3e75303977f1b8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 20 Jun 2025 14:30:25 -0400 Subject: [PATCH 072/216] Update people dashboard intro (#25856) --- src/translations/en.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 828f4ad0ac..609cb0e818 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5170,8 +5170,7 @@ }, "person": { "caption": "People", - "description": "Manage the people that Home Assistant tracks", - "introduction": "Here you can add each person of interest in Home Assistant.", + "introduction": "Manage the people Home Assistant recognizes and control their access.", "note_about_persons_configured_in_yaml": "People configured via configuration.yaml cannot be edited via the UI.", "learn_more": "Learn more about people", "no_persons_created_yet": "Looks like you have not added any people yet.", From a13dae474594be95496e041292f74c6e58d50324 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:30:39 +0000 Subject: [PATCH 073/216] Update vitest monorepo to v3.2.4 (#25857) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 140 +++++++++++++++++++++++++-------------------------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index 5af921e32f..ecd64bd56c 100644 --- a/package.json +++ b/package.json @@ -179,7 +179,7 @@ "@types/tar": "6.1.13", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", - "@vitest/coverage-v8": "3.2.3", + "@vitest/coverage-v8": "3.2.4", "babel-loader": "10.0.0", "babel-plugin-template-html-minifier": "4.1.0", "browserslist-useragent-regexp": "4.1.3", @@ -220,7 +220,7 @@ "typescript": "5.8.3", "typescript-eslint": "8.34.1", "vite-tsconfig-paths": "5.1.4", - "vitest": "3.2.3", + "vitest": "3.2.4", "webpack-stats-plugin": "1.1.3", "webpackbar": "7.0.0", "workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch" diff --git a/yarn.lock b/yarn.lock index d975b035fb..98d08ac89c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5294,9 +5294,9 @@ __metadata: languageName: node linkType: hard -"@vitest/coverage-v8@npm:3.2.3": - version: 3.2.3 - resolution: "@vitest/coverage-v8@npm:3.2.3" +"@vitest/coverage-v8@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/coverage-v8@npm:3.2.4" dependencies: "@ampproject/remapping": "npm:^2.3.0" "@bcoe/v8-coverage": "npm:^1.0.2" @@ -5312,33 +5312,33 @@ __metadata: test-exclude: "npm:^7.0.1" tinyrainbow: "npm:^2.0.0" peerDependencies: - "@vitest/browser": 3.2.3 - vitest: 3.2.3 + "@vitest/browser": 3.2.4 + vitest: 3.2.4 peerDependenciesMeta: "@vitest/browser": optional: true - checksum: 10/7d3ee1cd98e64be1725cf9d3b9f25faec18def7be93996ef6f963cbd0b52f96854d88d5e324ab2134bfcb7325fda6fd486e05cda1c8a03dfddc3484d4b3d2682 + checksum: 10/5a5940c78eabbb36efafb9ecc50408785614768b3f74f5f88e6dd32db59a21d39e15e7cf52fae961cc2cd75e0390c8568cdb9aef35aa8593ccd057edce539ee4 languageName: node linkType: hard -"@vitest/expect@npm:3.2.3": - version: 3.2.3 - resolution: "@vitest/expect@npm:3.2.3" +"@vitest/expect@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/expect@npm:3.2.4" dependencies: "@types/chai": "npm:^5.2.2" - "@vitest/spy": "npm:3.2.3" - "@vitest/utils": "npm:3.2.3" + "@vitest/spy": "npm:3.2.4" + "@vitest/utils": "npm:3.2.4" chai: "npm:^5.2.0" tinyrainbow: "npm:^2.0.0" - checksum: 10/c67318892c2441a53fd6386232a3392fd86faf3a17528ce70fa2b6ede7778b9d7e3142b463f722d0fb5516fb671f422549d34e674f4fe8721209101c5b69805d + checksum: 10/dc69ce886c13714dfbbff78f2d2cb7eb536017e82301a73c42d573a9e9d2bf91005ac7abd9b977adf0a3bd431209f45a8ac2418029b68b0a377e092607c843ce languageName: node linkType: hard -"@vitest/mocker@npm:3.2.3": - version: 3.2.3 - resolution: "@vitest/mocker@npm:3.2.3" +"@vitest/mocker@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/mocker@npm:3.2.4" dependencies: - "@vitest/spy": "npm:3.2.3" + "@vitest/spy": "npm:3.2.4" estree-walker: "npm:^3.0.3" magic-string: "npm:^0.30.17" peerDependencies: @@ -5349,58 +5349,58 @@ __metadata: optional: true vite: optional: true - checksum: 10/984203df788f9822d9d8ae0a85abc204f08770c63191d1300b6e05172021309b90321fddb5962101c268e66cc9f920982c0ccf768ee67a3d4c5cb44fb27b2934 + checksum: 10/5e92431b6ed9fc1679060e4caef3e4623f4750542a5d7cd944774f8217c4d231e273202e8aea00bab33260a5a9222ecb7005d80da0348c3c829bd37d123071a8 languageName: node linkType: hard -"@vitest/pretty-format@npm:3.2.3, @vitest/pretty-format@npm:^3.2.3": - version: 3.2.3 - resolution: "@vitest/pretty-format@npm:3.2.3" +"@vitest/pretty-format@npm:3.2.4, @vitest/pretty-format@npm:^3.2.4": + version: 3.2.4 + resolution: "@vitest/pretty-format@npm:3.2.4" dependencies: tinyrainbow: "npm:^2.0.0" - checksum: 10/fd39fa90f5ec486215c962ee05d80c31dc8bff84f4c29c4390a129757ac4cbbae09b4aa669982a7888fea5de1bb2c3532aefe6c13b7d28d45295f00793aea306 + checksum: 10/8dd30cbf956e01fbab042fe651fb5175d9f0cd00b7b569a46cd98df89c4fec47dab12916201ad6e09a4f25f2a2ec8927a4bfdc61118593097f759c90b18a51d4 languageName: node linkType: hard -"@vitest/runner@npm:3.2.3": - version: 3.2.3 - resolution: "@vitest/runner@npm:3.2.3" +"@vitest/runner@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/runner@npm:3.2.4" dependencies: - "@vitest/utils": "npm:3.2.3" + "@vitest/utils": "npm:3.2.4" pathe: "npm:^2.0.3" strip-literal: "npm:^3.0.0" - checksum: 10/4c4bdef49c646c47ef91683bf30797d48e2d1c2a615dc1adb730232578b7c4b00642204d2d15b6f73e70ecfda548da9a3c7883b50655d2801db19088f7769860 + checksum: 10/197bd55def519ef202f990b7c1618c212380831827c116240871033e4973decb780503c705ba9245a12bd8121f3ac4086ffcb3e302148b62d9bd77fd18dd1deb languageName: node linkType: hard -"@vitest/snapshot@npm:3.2.3": - version: 3.2.3 - resolution: "@vitest/snapshot@npm:3.2.3" +"@vitest/snapshot@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/snapshot@npm:3.2.4" dependencies: - "@vitest/pretty-format": "npm:3.2.3" + "@vitest/pretty-format": "npm:3.2.4" magic-string: "npm:^0.30.17" pathe: "npm:^2.0.3" - checksum: 10/64b085246172d5f1c3c9815062270988757f8f00244a678b81ad00cb7ffbb8df84767a508eac00ee9bf26485b8c4f600d19e3c76618f746270337cd1a7f82136 + checksum: 10/acfb682491b9ca9345bf9fed02c2779dec43e0455a380c1966b0aad8dd81c79960902cf34621ab48fe80a0eaf8c61cc42dec186a1321dc3c9897ef2ebd5f1bc4 languageName: node linkType: hard -"@vitest/spy@npm:3.2.3": - version: 3.2.3 - resolution: "@vitest/spy@npm:3.2.3" +"@vitest/spy@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/spy@npm:3.2.4" dependencies: tinyspy: "npm:^4.0.3" - checksum: 10/cff3d764c461f544cec692ff110c7dc0ee74dc5e1b6e228eba0e04b35819faf27c46321f1697f3a69010cdb609a87b7a32e852f0d07da9e510238c92006372c1 + checksum: 10/7d38c299f42a8c7e5e41652b203af98ca54e63df69c3b072d0e401d5a57fbbba3e39d8538ac1b3022c26718a6388d0bcc222bc2f07faab75942543b9247c007d languageName: node linkType: hard -"@vitest/utils@npm:3.2.3": - version: 3.2.3 - resolution: "@vitest/utils@npm:3.2.3" +"@vitest/utils@npm:3.2.4": + version: 3.2.4 + resolution: "@vitest/utils@npm:3.2.4" dependencies: - "@vitest/pretty-format": "npm:3.2.3" - loupe: "npm:^3.1.3" + "@vitest/pretty-format": "npm:3.2.4" + loupe: "npm:^3.1.4" tinyrainbow: "npm:^2.0.0" - checksum: 10/ac69c88082f2590c6793b600726b25edcb27a483b12a5aae6ce88b8fff64890aa4243ea14786659961be261303dcf312b2676674f8216d57e4f63ddf5a24aa28 + checksum: 10/7f12ef63bd8ee13957744d1f336b0405f164ade4358bf9dfa531f75bbb58ffac02bf61aba65724311ddbc50b12ba54853a169e59c6b837c16086173b9a480710 languageName: node linkType: hard @@ -9326,7 +9326,7 @@ __metadata: "@vaadin/combo-box": "npm:24.8.0" "@vaadin/vaadin-themable-mixin": "npm:24.8.0" "@vibrant/color": "npm:4.0.0" - "@vitest/coverage-v8": "npm:3.2.3" + "@vitest/coverage-v8": "npm:3.2.4" "@vue/web-component-wrapper": "npm:1.3.0" "@webcomponents/scoped-custom-element-registry": "npm:0.0.10" "@webcomponents/webcomponentsjs": "npm:2.8.0" @@ -9412,7 +9412,7 @@ __metadata: ua-parser-js: "npm:2.0.3" vis-data: "npm:7.1.9" vite-tsconfig-paths: "npm:5.1.4" - vitest: "npm:3.2.3" + vitest: "npm:3.2.4" vue: "npm:2.7.16" vue2-daterange-picker: "npm:0.6.8" webpack-stats-plugin: "npm:1.1.3" @@ -10952,10 +10952,10 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^3.1.0, loupe@npm:^3.1.3": - version: 3.1.3 - resolution: "loupe@npm:3.1.3" - checksum: 10/9e98c34daf0eba48ccc603595e51f2ae002110982d84879cf78c51de2c632f0c571dfe82ce4210af60c32203d06b443465c269bda925076fe6d9b612cc65c321 +"loupe@npm:^3.1.0, loupe@npm:^3.1.4": + version: 3.1.4 + resolution: "loupe@npm:3.1.4" + checksum: 10/06ab1893731f167f2ce71f464a8a68372dc4cb807ecae20f9b844660c93813a298ca76bcd747ba6568b057af725ea63f0034ba3140c8f1d1fbb482d797e593ef languageName: node linkType: hard @@ -14140,10 +14140,10 @@ __metadata: languageName: node linkType: hard -"tinypool@npm:^1.1.0": - version: 1.1.0 - resolution: "tinypool@npm:1.1.0" - checksum: 10/2e99e76f01699bb3244463a4b1b473fb9a166473d417b5ce373bbd12ef4626c221100533540d90f6bddbc83149ebf97e7ce052c0d1c5ae1a5066c5690cfee538 +"tinypool@npm:^1.1.1": + version: 1.1.1 + resolution: "tinypool@npm:1.1.1" + checksum: 10/0d54139e9dbc6ef33349768fa78890a4d708d16a7ab68e4e4ef3bb740609ddf0f9fd13292c2f413fbba756166c97051a657181c8f7ae92ade690604f183cc01d languageName: node linkType: hard @@ -14826,9 +14826,9 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:3.2.3": - version: 3.2.3 - resolution: "vite-node@npm:3.2.3" +"vite-node@npm:3.2.4": + version: 3.2.4 + resolution: "vite-node@npm:3.2.4" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.4.1" @@ -14837,7 +14837,7 @@ __metadata: vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" bin: vite-node: vite-node.mjs - checksum: 10/0dacd6b70cae010dadb560cf38afe33cef956a7cdf99fd099294891ae664b4c8bff7d217d7d85de8a042efcb0ac2d1cd9cea4901e8f48adf4356625c01b0a2aa + checksum: 10/343244ecabbab3b6e1a3065dabaeefa269965a7a7c54652d4b7a7207ee82185e887af97268c61755dcb2dd6a6ce5d9e114400cbd694229f38523e935703cc62f languageName: node linkType: hard @@ -14912,18 +14912,18 @@ __metadata: languageName: node linkType: hard -"vitest@npm:3.2.3": - version: 3.2.3 - resolution: "vitest@npm:3.2.3" +"vitest@npm:3.2.4": + version: 3.2.4 + resolution: "vitest@npm:3.2.4" dependencies: "@types/chai": "npm:^5.2.2" - "@vitest/expect": "npm:3.2.3" - "@vitest/mocker": "npm:3.2.3" - "@vitest/pretty-format": "npm:^3.2.3" - "@vitest/runner": "npm:3.2.3" - "@vitest/snapshot": "npm:3.2.3" - "@vitest/spy": "npm:3.2.3" - "@vitest/utils": "npm:3.2.3" + "@vitest/expect": "npm:3.2.4" + "@vitest/mocker": "npm:3.2.4" + "@vitest/pretty-format": "npm:^3.2.4" + "@vitest/runner": "npm:3.2.4" + "@vitest/snapshot": "npm:3.2.4" + "@vitest/spy": "npm:3.2.4" + "@vitest/utils": "npm:3.2.4" chai: "npm:^5.2.0" debug: "npm:^4.4.1" expect-type: "npm:^1.2.1" @@ -14934,17 +14934,17 @@ __metadata: tinybench: "npm:^2.9.0" tinyexec: "npm:^0.3.2" tinyglobby: "npm:^0.2.14" - tinypool: "npm:^1.1.0" + tinypool: "npm:^1.1.1" tinyrainbow: "npm:^2.0.0" vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" - vite-node: "npm:3.2.3" + vite-node: "npm:3.2.4" why-is-node-running: "npm:^2.3.0" peerDependencies: "@edge-runtime/vm": "*" "@types/debug": ^4.1.12 "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 - "@vitest/browser": 3.2.3 - "@vitest/ui": 3.2.3 + "@vitest/browser": 3.2.4 + "@vitest/ui": 3.2.4 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -14964,7 +14964,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10/7412aaa6f4c5d259b7329a7aba5466b3fd1fa67f4af0ee1ea14474c48f0c5f1b230fba2a5418a6c21df19c9b8268ba0d133f9d7aac553727914fa56664344b4a + checksum: 10/f10bbce093ecab310ecbe484536ef4496fb9151510b2be0c5907c65f6d31482d9c851f3182531d1d27d558054aa78e8efd9d4702ba6c82058657e8b6a52507ee languageName: node linkType: hard From 299713fd5e0fc71c03a4122e9e76096fa9ae3b34 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Fri, 20 Jun 2025 22:09:09 +0200 Subject: [PATCH 074/216] Fix inconsistently spelled occurrences of "add-on" (#25858) * Fix inconsistently spelled occurrences of "add-on" - add the missing hyphen on three occurrences of "addon" - change one occurrence of "Add-on" to lowercase * Also capitalize one inconsistent occurrence of "Ingress" --- src/translations/en.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 609cb0e818..964324323c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5680,7 +5680,7 @@ "network_settings_title": "Network settings", "change_channel": "Change channel", "channel_dialog": { - "title": "Multiprotocol addon in use", + "title": "Multiprotocol add-on in use", "text": "Zigbee and Thread share the same radio and must use the same channel. Change the channel of both networks by reconfiguring multiprotocol from the hardware menu." } }, @@ -9203,12 +9203,12 @@ "not_supported": "[%key:ui::panel::my::not_supported%]", "faq_link": "[%key:ui::panel::my::faq_link%]", "add_addon_repository_title": "Missing add-on repository", - "add_addon_repository_description": "The addon ''{addon}'' is a part of the add-on repository ''{repository}'', this repository is missing on your system, do you want to add that now?", + "add_addon_repository_description": "The add-on ''{addon}'' is a part of the add-on repository ''{repository}'', this repository is missing on your system, do you want to add that now?", "error": "[%key:ui::panel::my::error%]", "error_addon_not_found": "Add-on not found", - "error_repository_not_found": "The required repository for this Add-on was not found", + "error_repository_not_found": "The required repository for this add-on was not found", "error_addon_not_installed": "The requested add-on is not installed. Please install it first", - "error_addon_no_ingress": "The requested add-on does not support ingress" + "error_addon_no_ingress": "The requested add-on does not support Ingress" }, "ingress": { "error_addon_info": "Unable to fetch add-on info to start Ingress", @@ -9408,7 +9408,7 @@ }, "uninstall_addon": { "title": "Uninstall {name}?", - "remove_data": "Also permanently delete this addon's data", + "remove_data": "Also permanently delete this add-on's data", "uninstall": "Uninstall" }, "hardware": { From 1990472970b10e3ea1018100349cb876d5637121 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 20:43:30 +0000 Subject: [PATCH 075/216] Lock file maintenance (#25838) * Lock file maintenance --- src/common/datetime/format_date.ts | 2 +- yarn.lock | 872 +++++++++++++++-------------- 2 files changed, 453 insertions(+), 421 deletions(-) diff --git a/src/common/datetime/format_date.ts b/src/common/datetime/format_date.ts index 53b30f1deb..9b4b5bd510 100644 --- a/src/common/datetime/format_date.ts +++ b/src/common/datetime/format_date.ts @@ -77,7 +77,7 @@ export const formatDateNumeric = ( const month = parts.find((value) => value.type === "month")?.value; const year = parts.find((value) => value.type === "year")?.value; - const lastPart = parts.at(parts.length - 1); + const lastPart = parts[parts.length - 1]; let lastLiteral = lastPart?.type === "literal" ? lastPart?.value : ""; if (locale.language === "bg" && locale.date_format === DateFormat.YMD) { diff --git a/yarn.lock b/yarn.lock index 98d08ac89c..7c16f1005b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,16 +28,16 @@ __metadata: languageName: node linkType: hard -"@asamuzakjp/css-color@npm:^3.1.2": - version: 3.1.7 - resolution: "@asamuzakjp/css-color@npm:3.1.7" +"@asamuzakjp/css-color@npm:^3.2.0": + version: 3.2.0 + resolution: "@asamuzakjp/css-color@npm:3.2.0" dependencies: "@csstools/css-calc": "npm:^2.1.3" "@csstools/css-color-parser": "npm:^3.0.9" "@csstools/css-parser-algorithms": "npm:^3.0.4" "@csstools/css-tokenizer": "npm:^3.0.3" lru-cache: "npm:^10.4.3" - checksum: 10/107510bc16080917558d46c8ccb17dd932e7086999190ef733630a778dd83e12032805ef5d4b62729a718f0f8b806c3b0fc465693bd3d5b5180a3aa447bc1525 + checksum: 10/870f661460173174fef8bfebea0799ba26566f3aa7b307e5adabb7aae84fed2da68e40080104ed0c83b43c5be632ee409e65396af13bfe948a3ef4c2c729ecd9 languageName: node linkType: hard @@ -64,9 +64,9 @@ __metadata: linkType: hard "@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.27.2": - version: 7.27.2 - resolution: "@babel/compat-data@npm:7.27.2" - checksum: 10/eaa9f8aaeb9475779f4411fa397f712a6441b650d4e0b40c5535c954c891cd35c0363004db42902192aa8224532ac31ce06890478b060995286fe4fadd54e542 + version: 7.27.5 + resolution: "@babel/compat-data@npm:7.27.5" + checksum: 10/04c343b8a25955bbbe1569564c63ac481a74710eb2e7989b97bd10baf2f0f3b1aa1b6c6122749806e92d70cfc22c10c757ff62336eb10a28ea98ab2b82bc0c2c languageName: node linkType: hard @@ -94,24 +94,24 @@ __metadata: linkType: hard "@babel/generator@npm:^7.27.3": - version: 7.27.3 - resolution: "@babel/generator@npm:7.27.3" + version: 7.27.5 + resolution: "@babel/generator@npm:7.27.5" dependencies: - "@babel/parser": "npm:^7.27.3" + "@babel/parser": "npm:^7.27.5" "@babel/types": "npm:^7.27.3" "@jridgewell/gen-mapping": "npm:^0.3.5" "@jridgewell/trace-mapping": "npm:^0.3.25" jsesc: "npm:^3.0.2" - checksum: 10/3b8477ae0c305639f86aeb553115535b103626008945462d32171fa4ebd77f2a0345600dc5baee7ced98d54cc7da9c806808a04b555c75136f42e0e9d7794bdf + checksum: 10/f5e6942670cb32156b3ac2d75ce09b373558823387f15dd1413c27fe9eb5756a7c6011fc7f956c7acc53efb530bfb28afffa24364d46c4e9ffccc4e5c8b3b094 languageName: node linkType: hard "@babel/helper-annotate-as-pure@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/helper-annotate-as-pure@npm:7.27.1" + version: 7.27.3 + resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" dependencies: - "@babel/types": "npm:^7.27.1" - checksum: 10/3f8e4d591458d6c0621a3d670f8798b8895580214287390126e3e621ddf3df0bd07cbcc9500c2671b9ec10162c2f9feb1194da5cf039d40df8cb69d181fc0cd8 + "@babel/types": "npm:^7.27.3" + checksum: 10/63863a5c936ef82b546ca289c9d1b18fabfc24da5c4ee382830b124e2e79b68d626207febc8d4bffc720f50b2ee65691d7d12cc0308679dee2cd6bdc926b7190 languageName: node linkType: hard @@ -291,23 +291,23 @@ __metadata: linkType: hard "@babel/helpers@npm:^7.27.4": - version: 7.27.4 - resolution: "@babel/helpers@npm:7.27.4" + version: 7.27.6 + resolution: "@babel/helpers@npm:7.27.6" dependencies: "@babel/template": "npm:^7.27.2" - "@babel/types": "npm:^7.27.3" - checksum: 10/a697280575d338afd69461a07edb6db5b8623ca66528a9d2522e7b01476f42561b9563ab688b3ebe9e6afd4d25c6e66cc29a4466c424ab0d6573180c2bfdec0f + "@babel/types": "npm:^7.27.6" + checksum: 10/33c1ab2b42f05317776a4d67c5b00d916dbecfbde38a9406a1300ad3ad6e0380a2f6fcd3361369119a82a7d3c20de6e66552d147297f17f656cf17912605aa97 languageName: node linkType: hard -"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.3, @babel/parser@npm:^7.27.4": - version: 7.27.4 - resolution: "@babel/parser@npm:7.27.4" +"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.4, @babel/parser@npm:^7.27.5": + version: 7.27.5 + resolution: "@babel/parser@npm:7.27.5" dependencies: "@babel/types": "npm:^7.27.3" bin: parser: ./bin/babel-parser.js - checksum: 10/5ff6db87fd17de99792bf9a54480feeb069fc90ffa64ce96524c7437222549c86dde10fc1c945d4e9a94f3f2fc6ee4b3e1cfaf372f844bd054791e30089e92cf + checksum: 10/0ad671be7994dba7d31ec771bd70ea5090aa34faf73e93b1b072e3c0a704ab69f4a7a68ebfb9d6a7fa455e0aa03dfa65619c4df6bae1cf327cba925b1d233fc4 languageName: node linkType: hard @@ -462,13 +462,13 @@ __metadata: linkType: hard "@babel/plugin-transform-block-scoping@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/plugin-transform-block-scoping@npm:7.27.1" + version: 7.27.5 + resolution: "@babel/plugin-transform-block-scoping@npm:7.27.5" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/1afed217e44de8d1ad30eccd9d3ba30627798718db3c58430413ebaeb3488620bf2b16c5bfb54b1bb8935635fb5583c9dd53ea8588e9674e6c9478e7b03f94ca + checksum: 10/bc911f0aa15bc9a5e0e1130681c1a6abd05300f6c8c02af9c97b0eaaae43b0f2936b34a5efc1a166a8e296c421c574a0e04dd0d6dc62adaab1246a387e6cfe26 languageName: node linkType: hard @@ -524,14 +524,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/plugin-transform-destructuring@npm:7.27.1" +"@babel/plugin-transform-destructuring@npm:^7.27.1, @babel/plugin-transform-destructuring@npm:^7.27.3": + version: 7.27.3 + resolution: "@babel/plugin-transform-destructuring@npm:7.27.3" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/8a128e80135985a9a8f403f8c684f65c0eacb6d5295567c38b1b67dc8717821894c8a004977381c7bb82c647678521f063c981afd9d1141b25df838ad0e8c1b2 + checksum: 10/d5b1868d079551c0a2e923419613efe18a987548219bb378c61ab7e005d4f3ea590067f93996df6d896177c1cae1396b4aae9163c8a4ee77e9ffbc11a78fb88d languageName: node linkType: hard @@ -768,16 +768,16 @@ __metadata: linkType: hard "@babel/plugin-transform-object-rest-spread@npm:^7.27.2": - version: 7.27.2 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.27.2" + version: 7.27.3 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.27.3" dependencies: "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-plugin-utils": "npm:^7.27.1" - "@babel/plugin-transform-destructuring": "npm:^7.27.1" + "@babel/plugin-transform-destructuring": "npm:^7.27.3" "@babel/plugin-transform-parameters": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/284a458f229e2acabe8a4d3a6cb1cb510dd1d76dc652d220865b8008b4cad923cea5faf76a8990810c2f99704797fbebb2ff15d482c448193691985a71226ecf + checksum: 10/7cc7be29a99010aac04fd78383f06d550b26460ea5367489e58ae484f0ed2f176966f0196bea0c2114a9872dd854a482bca38a9fad661c9d10d102c7195d53fd languageName: node linkType: hard @@ -864,13 +864,13 @@ __metadata: linkType: hard "@babel/plugin-transform-regenerator@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/plugin-transform-regenerator@npm:7.27.1" + version: 7.27.5 + resolution: "@babel/plugin-transform-regenerator@npm:7.27.5" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/a6a3f818e4cc5ac5a01962a1d66515cb6791f32eb1dcd810dbb39f6e580c0c96b47fcc5c5633137e040f4e137e40e6f4dad8a3d57d49b15aed40e72e13e30d93 + checksum: 10/ae4e203df1cb44418001fc0f5c75d7079ab342a1d629d6c0f581a3e521d0f6e5f7d5b351cb009e396782db579b29ceb66f260a873e0b8cd4c6901449af7edaa2 languageName: node linkType: hard @@ -1141,13 +1141,13 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.4.4": - version: 7.27.3 - resolution: "@babel/types@npm:7.27.3" +"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6, @babel/types@npm:^7.4.4": + version: 7.27.6 + resolution: "@babel/types@npm:7.27.6" dependencies: "@babel/helper-string-parser": "npm:^7.27.1" "@babel/helper-validator-identifier": "npm:^7.27.1" - checksum: 10/a24e6accd85c4747b974b3d68a3210d0aa1180c1a77b287ffcb7401cd2edad7bdecadaeb40fe5191be3990c3a5252943f7de7c09da13ed269adbb054b97056ee + checksum: 10/174741c667775680628a09117828bbeffb35ea543f59bf80649d0d60672f7815a0740ddece3cca87516199033a039166a6936434131fce2b6a820227e64f91ae languageName: node linkType: hard @@ -1262,42 +1262,42 @@ __metadata: languageName: node linkType: hard -"@csstools/css-calc@npm:^2.1.3": - version: 2.1.3 - resolution: "@csstools/css-calc@npm:2.1.3" +"@csstools/css-calc@npm:^2.1.3, @csstools/css-calc@npm:^2.1.4": + version: 2.1.4 + resolution: "@csstools/css-calc@npm:2.1.4" peerDependencies: - "@csstools/css-parser-algorithms": ^3.0.4 - "@csstools/css-tokenizer": ^3.0.3 - checksum: 10/0c20165f13135bb51ef397c4ea8e185c75ff379378212952af57052de96890a1eda056b2c6a2d573ea69e56c9dae79a906a2e4cac9d731dfbf19defaf943fd55 + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/06975b650c0f44c60eeb7afdb3fd236f2dd607b2c622e0bc908d3f54de39eb84e0692833320d03dac04bd6c1ab0154aa3fa0dd442bd9e5f917cf14d8e2ba8d74 languageName: node linkType: hard "@csstools/css-color-parser@npm:^3.0.9": - version: 3.0.9 - resolution: "@csstools/css-color-parser@npm:3.0.9" + version: 3.0.10 + resolution: "@csstools/css-color-parser@npm:3.0.10" dependencies: "@csstools/color-helpers": "npm:^5.0.2" - "@csstools/css-calc": "npm:^2.1.3" + "@csstools/css-calc": "npm:^2.1.4" peerDependencies: - "@csstools/css-parser-algorithms": ^3.0.4 - "@csstools/css-tokenizer": ^3.0.3 - checksum: 10/634ee3c5424e21bda414015d20e906a620d06186fe38957479a5266ded435ae14675e3085a259cec75cd7138df081357aba58a2626592d61335228a451db3eca + "@csstools/css-parser-algorithms": ^3.0.5 + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/d5619639f067c0a6ac95ecce6ad6adce55a5500599a4444817ac6bb5ed2a9928a08f0978a148d4687de7ebf05c068c1a1c7f9eaa039984830a84148e011cbc05 languageName: node linkType: hard "@csstools/css-parser-algorithms@npm:^3.0.4": - version: 3.0.4 - resolution: "@csstools/css-parser-algorithms@npm:3.0.4" + version: 3.0.5 + resolution: "@csstools/css-parser-algorithms@npm:3.0.5" peerDependencies: - "@csstools/css-tokenizer": ^3.0.3 - checksum: 10/dfb6926218d9f8ba25d8b43ea46c03863c819481f8c55e4de4925780eaab9e6bcd6bead1d56b4ef82d09fcd9d69a7db2750fa9db08eece9470fd499dc76d0edb + "@csstools/css-tokenizer": ^3.0.4 + checksum: 10/e93083b5cb36a3c1e7a47ce10cf62961d05bd1e4c608bb3ee50186ff740157ab0ec16a3956f7b86251efd10703034d849693201eea858ae904848c68d2d46ada languageName: node linkType: hard "@csstools/css-tokenizer@npm:^3.0.3": - version: 3.0.3 - resolution: "@csstools/css-tokenizer@npm:3.0.3" - checksum: 10/6baa3160e426e1f177b8f10d54ec7a4a596090f65a05f16d7e9e4da049962a404eabc5f885f4867093702c259cd4080ac92a438326e22dea015201b3e71f5bbb + version: 3.0.4 + resolution: "@csstools/css-tokenizer@npm:3.0.4" + checksum: 10/eb6c84c086312f6bb8758dfe2c85addd7475b0927333c5e39a4d59fb210b9810f8c346972046f95e60a721329cffe98895abe451e51de753ad1ca7a8c24ec65f languageName: node linkType: hard @@ -1324,177 +1324,177 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/aix-ppc64@npm:0.25.4" +"@esbuild/aix-ppc64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/aix-ppc64@npm:0.25.5" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/android-arm64@npm:0.25.4" +"@esbuild/android-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-arm64@npm:0.25.5" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/android-arm@npm:0.25.4" +"@esbuild/android-arm@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-arm@npm:0.25.5" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/android-x64@npm:0.25.4" +"@esbuild/android-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-x64@npm:0.25.5" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/darwin-arm64@npm:0.25.4" +"@esbuild/darwin-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/darwin-arm64@npm:0.25.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/darwin-x64@npm:0.25.4" +"@esbuild/darwin-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/darwin-x64@npm:0.25.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/freebsd-arm64@npm:0.25.4" +"@esbuild/freebsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/freebsd-arm64@npm:0.25.5" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/freebsd-x64@npm:0.25.4" +"@esbuild/freebsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/freebsd-x64@npm:0.25.5" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-arm64@npm:0.25.4" +"@esbuild/linux-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-arm64@npm:0.25.5" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-arm@npm:0.25.4" +"@esbuild/linux-arm@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-arm@npm:0.25.5" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-ia32@npm:0.25.4" +"@esbuild/linux-ia32@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-ia32@npm:0.25.5" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-loong64@npm:0.25.4" +"@esbuild/linux-loong64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-loong64@npm:0.25.5" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-mips64el@npm:0.25.4" +"@esbuild/linux-mips64el@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-mips64el@npm:0.25.5" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-ppc64@npm:0.25.4" +"@esbuild/linux-ppc64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-ppc64@npm:0.25.5" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-riscv64@npm:0.25.4" +"@esbuild/linux-riscv64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-riscv64@npm:0.25.5" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-s390x@npm:0.25.4" +"@esbuild/linux-s390x@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-s390x@npm:0.25.5" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-x64@npm:0.25.4" +"@esbuild/linux-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-x64@npm:0.25.5" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/netbsd-arm64@npm:0.25.4" +"@esbuild/netbsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/netbsd-arm64@npm:0.25.5" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/netbsd-x64@npm:0.25.4" +"@esbuild/netbsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/netbsd-x64@npm:0.25.5" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/openbsd-arm64@npm:0.25.4" +"@esbuild/openbsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/openbsd-arm64@npm:0.25.5" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/openbsd-x64@npm:0.25.4" +"@esbuild/openbsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/openbsd-x64@npm:0.25.5" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/sunos-x64@npm:0.25.4" +"@esbuild/sunos-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/sunos-x64@npm:0.25.5" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/win32-arm64@npm:0.25.4" +"@esbuild/win32-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-arm64@npm:0.25.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/win32-ia32@npm:0.25.4" +"@esbuild/win32-ia32@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-ia32@npm:0.25.5" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/win32-x64@npm:0.25.4" +"@esbuild/win32-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-x64@npm:0.25.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1529,9 +1529,9 @@ __metadata: linkType: hard "@eslint/config-helpers@npm:^0.2.1": - version: 0.2.2 - resolution: "@eslint/config-helpers@npm:0.2.2" - checksum: 10/55dbb0b8d63c4cb28fa2a5fd5f16c785f6bd87eb0f50d2f42ec3f7d06b5c6201e2e170846a4360ca00105578b034fba132ed54e4ee3215be240c4a43e7839189 + version: 0.2.3 + resolution: "@eslint/config-helpers@npm:0.2.3" + checksum: 10/1f5082248f65555cc666942f7c991a2cfd6821758fb45338f43b28ea0f6b77d0c48b35097400d9b8fe1b4b10150085452e0b8f2d6d9ba17a84e16a6c7e4b341d languageName: node linkType: hard @@ -1544,6 +1544,15 @@ __metadata: languageName: node linkType: hard +"@eslint/core@npm:^0.15.0": + version: 0.15.0 + resolution: "@eslint/core@npm:0.15.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10/27c9cb5bdc5c9dead5b06f2b2a6a66d8bbe5e2e19397e2c5ff9ea582c9d4e4478bf1bc1bdd4eaec7bb3a0d6fa53f152e595acf637354776c14bb58c321ea5aa3 + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^3.3.1": version: 3.3.1 resolution: "@eslint/eslintrc@npm:3.3.1" @@ -1576,31 +1585,31 @@ __metadata: linkType: hard "@eslint/plugin-kit@npm:^0.3.1": - version: 0.3.1 - resolution: "@eslint/plugin-kit@npm:0.3.1" + version: 0.3.2 + resolution: "@eslint/plugin-kit@npm:0.3.2" dependencies: - "@eslint/core": "npm:^0.14.0" + "@eslint/core": "npm:^0.15.0" levn: "npm:^0.4.1" - checksum: 10/ab0c4cecadc6c38c7ae5f71b9831d3521d08237444d8f327751d1133a4369ccd42093a1c06b26fd6c311015807a27d95a0184a761d1cdd264b090896dcf0addb + checksum: 10/26ba99936f72ca124036fbc5ca93168713fab5984117109b1447642a93725fbb75aa457622683dc8797509e40294497d74b584caa26f285373bdde17ceba8eac languageName: node linkType: hard -"@floating-ui/core@npm:^1.7.0": - version: 1.7.0 - resolution: "@floating-ui/core@npm:1.7.0" +"@floating-ui/core@npm:^1.7.1": + version: 1.7.1 + resolution: "@floating-ui/core@npm:1.7.1" dependencies: "@floating-ui/utils": "npm:^0.2.9" - checksum: 10/b047b9b5e18d9c2b737aa1cd0aadd138da15e6ca4ba35cc8b41eff280a66f84c749739234d782e8f250de0d6d5afed4b1d06ed9bf2635e0743aa9868d32fe94a + checksum: 10/5dbe5d92dcdaef6a915a6bfaa432a684b0a021e6eca0eab796216eecb0870282f8b9ecfcf449f1cac94cc24d8c5114d1677b1f7a6e11e2642967065f2497ce26 languageName: node linkType: hard "@floating-ui/dom@npm:^1.6.12": - version: 1.7.0 - resolution: "@floating-ui/dom@npm:1.7.0" + version: 1.7.1 + resolution: "@floating-ui/dom@npm:1.7.1" dependencies: - "@floating-ui/core": "npm:^1.7.0" + "@floating-ui/core": "npm:^1.7.1" "@floating-ui/utils": "npm:^0.2.9" - checksum: 10/9c3561981ea389fe39b7095d7a3771fd906580eedc43fda0eb554faa5f2f9756169fef1824d66198314a41c7f8d63e56bc7f5b24fc528471c9e6bc43cafa6a00 + checksum: 10/77f385e0202855aaeee7c8c96e40c8cd06c63f1946ed666824beed40b98e9414a5a8c19ac8c8f68653577eceb1866261a785d3d9855a531bd85d2865024ca9e9 languageName: node linkType: hard @@ -3372,13 +3381,13 @@ __metadata: linkType: hard "@octokit/plugin-paginate-rest@npm:^13.0.1": - version: 13.0.1 - resolution: "@octokit/plugin-paginate-rest@npm:13.0.1" + version: 13.1.0 + resolution: "@octokit/plugin-paginate-rest@npm:13.1.0" dependencies: "@octokit/types": "npm:^14.1.0" peerDependencies: "@octokit/core": ">=6" - checksum: 10/eb58db6bbe69ccb7ac4f73ddc20f4e491d26cdef820d0676a5682ecfe01c486a3a3059cc5151802dc6efeb2b4766eac84d05eafc9a35ac4855cb4b73b29ce809 + checksum: 10/1e34afb0fa619462bbfc1dda65774df25762c05f61f4e2967124ce92e765e08d1bd3f7534e681038128b686e70aea19a41922835f9028ca39c49ecc82e82ed0b languageName: node linkType: hard @@ -3425,15 +3434,15 @@ __metadata: linkType: hard "@octokit/request@npm:^10.0.2": - version: 10.0.2 - resolution: "@octokit/request@npm:10.0.2" + version: 10.0.3 + resolution: "@octokit/request@npm:10.0.3" dependencies: "@octokit/endpoint": "npm:^11.0.0" "@octokit/request-error": "npm:^7.0.0" "@octokit/types": "npm:^14.0.0" fast-content-type-parse: "npm:^3.0.0" universal-user-agent: "npm:^7.0.2" - checksum: 10/eaddfd49787e8caad664a80c7c665d69bd303f90b5e6be822d571b684a4cd42bdfee29119f838fdfaed2946bc09f38219e1d7a0923388436bff0bfdd0202acca + checksum: 10/f32a2c3fe97e0354390c0748a443e2f600a4e169b1014deb0b668ac3c52aa25cab523e87508751a1247806e3998c41f8849ad41ee3da531936975f5d32ab4c02 languageName: node linkType: hard @@ -3583,8 +3592,8 @@ __metadata: linkType: hard "@rollup/pluginutils@npm:^5.0.1": - version: 5.1.4 - resolution: "@rollup/pluginutils@npm:5.1.4" + version: 5.2.0 + resolution: "@rollup/pluginutils@npm:5.2.0" dependencies: "@types/estree": "npm:^1.0.0" estree-walker: "npm:^2.0.2" @@ -3594,146 +3603,146 @@ __metadata: peerDependenciesMeta: rollup: optional: true - checksum: 10/598f628988af25541a9a6c6ef154aaf350f8be3238884e500cc0e47138684071abe490563c953f9bda9e8b113ecb1f99c11abfb9dbaf4f72cdd62e257a673fa3 + checksum: 10/15e98a9e7ebeb9fdbbf072ad40e72947654abf98bcd389d6e54dcffe28f7eb93d9653037d5e18b703b0160e04210a1995cf08fc2bf45601ce77b17e4461f55c0 languageName: node linkType: hard -"@rollup/rollup-android-arm-eabi@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-android-arm-eabi@npm:4.42.0" +"@rollup/rollup-android-arm-eabi@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.44.0" conditions: os=android & cpu=arm languageName: node linkType: hard -"@rollup/rollup-android-arm64@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-android-arm64@npm:4.42.0" +"@rollup/rollup-android-arm64@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-android-arm64@npm:4.44.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-arm64@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-darwin-arm64@npm:4.42.0" +"@rollup/rollup-darwin-arm64@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-darwin-arm64@npm:4.44.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-darwin-x64@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-darwin-x64@npm:4.42.0" +"@rollup/rollup-darwin-x64@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-darwin-x64@npm:4.44.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-freebsd-arm64@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-freebsd-arm64@npm:4.42.0" +"@rollup/rollup-freebsd-arm64@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-freebsd-arm64@npm:4.44.0" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-freebsd-x64@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-freebsd-x64@npm:4.42.0" +"@rollup/rollup-freebsd-x64@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-freebsd-x64@npm:4.44.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rollup/rollup-linux-arm-gnueabihf@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.42.0" +"@rollup/rollup-linux-arm-gnueabihf@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.44.0" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm-musleabihf@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.42.0" +"@rollup/rollup-linux-arm-musleabihf@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.44.0" conditions: os=linux & cpu=arm & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-arm64-gnu@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.42.0" +"@rollup/rollup-linux-arm64-gnu@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.44.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-arm64-musl@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-arm64-musl@npm:4.42.0" +"@rollup/rollup-linux-arm64-musl@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.44.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-loongarch64-gnu@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.42.0" +"@rollup/rollup-linux-loongarch64-gnu@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.44.0" conditions: os=linux & cpu=loong64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-powerpc64le-gnu@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.42.0" +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.0" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-gnu@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.42.0" +"@rollup/rollup-linux-riscv64-gnu@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.44.0" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-riscv64-musl@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.42.0" +"@rollup/rollup-linux-riscv64-musl@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.44.0" conditions: os=linux & cpu=riscv64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-linux-s390x-gnu@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.42.0" +"@rollup/rollup-linux-s390x-gnu@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.44.0" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-gnu@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-x64-gnu@npm:4.42.0" +"@rollup/rollup-linux-x64-gnu@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.44.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rollup/rollup-linux-x64-musl@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-linux-x64-musl@npm:4.42.0" +"@rollup/rollup-linux-x64-musl@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.44.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rollup/rollup-win32-arm64-msvc@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.42.0" +"@rollup/rollup-win32-arm64-msvc@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.44.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rollup/rollup-win32-ia32-msvc@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.42.0" +"@rollup/rollup-win32-ia32-msvc@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.44.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rollup/rollup-win32-x64-msvc@npm:4.42.0": - version: 4.42.0 - resolution: "@rollup/rollup-win32-x64-msvc@npm:4.42.0" +"@rollup/rollup-win32-x64-msvc@npm:4.44.0": + version: 4.44.0 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.44.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -4306,12 +4315,12 @@ __metadata: linkType: hard "@types/body-parser@npm:*": - version: 1.19.5 - resolution: "@types/body-parser@npm:1.19.5" + version: 1.19.6 + resolution: "@types/body-parser@npm:1.19.6" dependencies: "@types/connect": "npm:*" "@types/node": "npm:*" - checksum: 10/1e251118c4b2f61029cc43b0dc028495f2d1957fe8ee49a707fb940f86a9bd2f9754230805598278fe99958b49e9b7e66eec8ef6a50ab5c1f6b93e1ba2aaba82 + checksum: 10/33041e88eae00af2cfa0827e951e5f1751eafab2a8b6fce06cd89ef368a988907996436b1325180edaeddd1c0c7d0d0d4c20a6c9ff294a91e0039a9db9e9b658 languageName: node linkType: hard @@ -4334,12 +4343,12 @@ __metadata: linkType: hard "@types/chrome@npm:*": - version: 0.0.323 - resolution: "@types/chrome@npm:0.0.323" + version: 0.0.326 + resolution: "@types/chrome@npm:0.0.326" dependencies: "@types/filesystem": "npm:*" "@types/har-format": "npm:*" - checksum: 10/cb09607816b1a501f234cc376cb5b7e91a04f0ff91d0feda58b0b28169fa0b411e61543d77e760ebd98776fde3e798aec4c552b67f3022a04ddbeafe5e2a6b47 + checksum: 10/88cd7ca8e3ceae180216ce92510f3a2d32eb1fbc8811801e09e6713d47aa227fc2b128bf9c55f7d5e71458d78ef4ab2cad5e1cf5238cc1ca877580d693b523b8 languageName: node linkType: hard @@ -4386,11 +4395,11 @@ __metadata: linkType: hard "@types/cors@npm:^2.8.12": - version: 2.8.18 - resolution: "@types/cors@npm:2.8.18" + version: 2.8.19 + resolution: "@types/cors@npm:2.8.19" dependencies: "@types/node": "npm:*" - checksum: 10/6e49b741345e67834cd19d766228509e4b37d6d5c272355bb059502b4787f5adf58776d9114ac5f0f407966e0347ae8d1f995d7ea41e6a24f716d36b3010401b + checksum: 10/9545cc532c9218754443f48a0c98c1a9ba4af1fe54a3425c95de75ff3158147bb39e666cb7c6bf98cc56a9c6dc7b4ce5b2cbdae6b55d5942e50c81b76ed6b825 languageName: node linkType: hard @@ -4422,10 +4431,10 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:1.0.7, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6": - version: 1.0.7 - resolution: "@types/estree@npm:1.0.7" - checksum: 10/419c845ece767ad4b21171e6e5b63dabb2eb46b9c0d97361edcd9cabbf6a95fcadb91d89b5fa098d1336fa0b8fceaea82fca97a2ef3971f5c86e53031e157b21 +"@types/estree@npm:1.0.8, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.6": + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10/25a4c16a6752538ffde2826c2cc0c6491d90e69cd6187bef4a006dd2c3c45469f049e643d7e516c515f21484dc3d48fd5c870be158a5beb72f5baf3dc43e4099 languageName: node linkType: hard @@ -4454,25 +4463,25 @@ __metadata: linkType: hard "@types/express@npm:*": - version: 5.0.2 - resolution: "@types/express@npm:5.0.2" + version: 5.0.3 + resolution: "@types/express@npm:5.0.3" dependencies: "@types/body-parser": "npm:*" "@types/express-serve-static-core": "npm:^5.0.0" "@types/serve-static": "npm:*" - checksum: 10/c6f73c75c2b62e293e5fadcfd7de61716e3d0c0eaecda082b1921c9c8e56ddc37c530c5d4d68404794b38c7e8f5b893719ffc04c6fba2efa2fb13223bfb0346b + checksum: 10/bb6f10c14c8e3cce07f79ee172688aa9592852abd7577b663cd0c2054307f172c2b2b36468c918fed0d4ac359b99695807b384b3da6157dfa79acbac2226b59b languageName: node linkType: hard "@types/express@npm:^4.17.21": - version: 4.17.22 - resolution: "@types/express@npm:4.17.22" + version: 4.17.23 + resolution: "@types/express@npm:4.17.23" dependencies: "@types/body-parser": "npm:*" "@types/express-serve-static-core": "npm:^4.17.33" "@types/qs": "npm:*" "@types/serve-static": "npm:*" - checksum: 10/9497634fc341ff4ac966ec0c529ded03bdacd2c3dae164f10a060ff250c66591b873aedce92d0239869cf3d05615ae9bcad584c7349fe68780242f6fef010c62 + checksum: 10/cf4d540bbd90801cdc79a46107b8873404698a7fd0c3e8dd42989d52d3bd7f5b8768672e54c20835e41e27349c319bb47a404ad14c0f8db0e9d055ba1cb8a05b languageName: node linkType: hard @@ -4541,9 +4550,9 @@ __metadata: linkType: hard "@types/http-errors@npm:*": - version: 2.0.4 - resolution: "@types/http-errors@npm:2.0.4" - checksum: 10/1f3d7c3b32c7524811a45690881736b3ef741bf9849ae03d32ad1ab7062608454b150a4e7f1351f83d26a418b2d65af9bdc06198f1c079d75578282884c4e8e3 + version: 2.0.5 + resolution: "@types/http-errors@npm:2.0.5" + checksum: 10/a88da669366bc483e8f3b3eb3d34ada5f8d13eeeef851b1204d77e2ba6fc42aba4566d877cca5c095204a3f4349b87fe397e3e21288837bdd945dd514120755b languageName: node linkType: hard @@ -4632,9 +4641,9 @@ __metadata: linkType: hard "@types/lodash@npm:*": - version: 4.17.16 - resolution: "@types/lodash@npm:4.17.16" - checksum: 10/9a8bb7471a7521bd65d528e1bd14f79819a3eeb6f8a35a8a44649a7d773775c0813e93fd93bd32ccf350bb076c0bf02c6d47877c4625f526f6dd4d283c746aec + version: 4.17.18 + resolution: "@types/lodash@npm:4.17.18" + checksum: 10/54ebb15b29925112dbe9da3abd99fb80d7202bc5ba20fc1b4fc8ea835d0012f00cbd9a3e7f367b70e7c3f2d5ee635964e3920a489625647b558f02994b3dd381 languageName: node linkType: hard @@ -4686,11 +4695,11 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>=10.0.0": - version: 22.15.19 - resolution: "@types/node@npm:22.15.19" + version: 24.0.3 + resolution: "@types/node@npm:24.0.3" dependencies: - undici-types: "npm:~6.21.0" - checksum: 10/02311c2b5dbf2e9e2c17497dc27858bcefbe12a81af0d9b81f865613d8d014726e0eb6cbebfbdb84a327c1b9f9da1347a65a7699ac58c8854fb4daf447031149 + undici-types: "npm:~7.8.0" + checksum: 10/6cce0afa9b0ff7f8eab7cb0339909c1e4ef480b824b8de5adc9cee05dac63ee3d8c7a46e1f95f13ecc94e84608118741f9949527a92fbf3f0e1f7714b37a7b61 languageName: node linkType: hard @@ -4702,11 +4711,11 @@ __metadata: linkType: hard "@types/node@npm:^18.15.3": - version: 18.19.101 - resolution: "@types/node@npm:18.19.101" + version: 18.19.112 + resolution: "@types/node@npm:18.19.112" dependencies: undici-types: "npm:~5.26.4" - checksum: 10/fc56ee1895afe249e2a2b6712df65477345f1ccd0d1f9a13cb0c4079a2513fdf14e1dcbc4180de746caae14fcd5d03840959a29b4779b1155fd787648af319c5 + checksum: 10/1d0150b4afbfa76ddcdbdcfaaa695dd1dc7485047d0c7e0b22207a0ffb61dab5bc44d536e4d2c3cb85c91ebb519479bfcd7033e76054fbc96fa6d13a86d9b26d languageName: node linkType: hard @@ -4778,12 +4787,12 @@ __metadata: linkType: hard "@types/send@npm:*": - version: 0.17.4 - resolution: "@types/send@npm:0.17.4" + version: 0.17.5 + resolution: "@types/send@npm:0.17.5" dependencies: "@types/mime": "npm:^1" "@types/node": "npm:*" - checksum: 10/28320a2aa1eb704f7d96a65272a07c0bf3ae7ed5509c2c96ea5e33238980f71deeed51d3631927a77d5250e4091b3e66bce53b42d770873282c6a20bb8b0280d + checksum: 10/b68ae8f9ba9328a4f276cd010914ed43b96371fbf34c7aa08a9111bff36661810bb14b96647e4a92e319dbd2689dc107fb0f9194ec3fa9335c162dc134026240 languageName: node linkType: hard @@ -4797,13 +4806,13 @@ __metadata: linkType: hard "@types/serve-static@npm:*, @types/serve-static@npm:^1.15.5": - version: 1.15.7 - resolution: "@types/serve-static@npm:1.15.7" + version: 1.15.8 + resolution: "@types/serve-static@npm:1.15.8" dependencies: "@types/http-errors": "npm:*" "@types/node": "npm:*" "@types/send": "npm:*" - checksum: 10/c5a7171d5647f9fbd096ed1a26105759f3153ccf683824d99fee4c7eb9cde2953509621c56a070dd9fb1159e799e86d300cbe4e42245ebc5b0c1767e8ca94a67 + checksum: 10/c031f870df6056a4c0a5a0ae94c5584006ab55400c74ae44de4d68d89338fbe982422861bad478b89a073f671efca454689fd28b6147358d6adc8edbc599caea languageName: node linkType: hard @@ -5799,16 +5808,18 @@ __metadata: linkType: hard "array-includes@npm:^3.1.8": - version: 3.1.8 - resolution: "array-includes@npm:3.1.8" + version: 3.1.9 + resolution: "array-includes@npm:3.1.9" dependencies: - call-bind: "npm:^1.0.7" + call-bind: "npm:^1.0.8" + call-bound: "npm:^1.0.4" define-properties: "npm:^1.2.1" - es-abstract: "npm:^1.23.2" - es-object-atoms: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.4" - is-string: "npm:^1.0.7" - checksum: 10/290b206c9451f181fb2b1f79a3bf1c0b66bb259791290ffbada760c79b284eef6f5ae2aeb4bcff450ebc9690edd25732c4c73a3c2b340fcc0f4563aed83bf488 + es-abstract: "npm:^1.24.0" + es-object-atoms: "npm:^1.1.1" + get-intrinsic: "npm:^1.3.0" + is-string: "npm:^1.1.1" + math-intrinsics: "npm:^1.1.0" + checksum: 10/8bfe9a58df74f326b4a76b04ee05c13d871759e888b4ee8f013145297cf5eb3c02cfa216067ebdaac5d74eb9763ac5cad77cdf2773b8ab475833701e032173aa languageName: node linkType: hard @@ -5963,13 +5974,13 @@ __metadata: linkType: hard "axios@npm:^1.9.0": - version: 1.9.0 - resolution: "axios@npm:1.9.0" + version: 1.10.0 + resolution: "axios@npm:1.10.0" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10/a2f90bba56820883879f32a237e2b9ff25c250365dcafd41cec41b3406a3df334a148f90010182dfdadb4b41dc59f6f0b3e8898ff41b666d1157b5f3f4523497 + checksum: 10/d43c80316a45611fd395743e15d16ea69a95f2b7f7095f2bb12cb78f9ca0a905194a02e52a3bf4e0db9f85fd1186d6c690410644c10ecd8bb0a468e57c2040e4 languageName: node linkType: hard @@ -6187,21 +6198,21 @@ __metadata: linkType: hard "brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" + version: 1.1.12 + resolution: "brace-expansion@npm:1.1.12" dependencies: balanced-match: "npm:^1.0.0" concat-map: "npm:0.0.1" - checksum: 10/faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + checksum: 10/12cb6d6310629e3048cadb003e1aca4d8c9bb5c67c3c321bafdd7e7a50155de081f78ea3e0ed92ecc75a9015e784f301efc8132383132f4f7904ad1ac529c562 languageName: node linkType: hard "brace-expansion@npm:^2.0.1": - version: 2.0.1 - resolution: "brace-expansion@npm:2.0.1" + version: 2.0.2 + resolution: "brace-expansion@npm:2.0.2" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10/a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + checksum: 10/01dff195e3646bc4b0d27b63d9bab84d2ebc06121ff5013ad6e5356daa5a9d6b60fa26cf73c74797f2dc3fbec112af13578d51f75228c1112b26c790a87b0488 languageName: node linkType: hard @@ -6248,17 +6259,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.1, browserslist@npm:^4.24.0, browserslist@npm:^4.24.4": - version: 4.24.5 - resolution: "browserslist@npm:4.24.5" +"browserslist@npm:^4.23.1, browserslist@npm:^4.24.0, browserslist@npm:^4.25.0": + version: 4.25.0 + resolution: "browserslist@npm:4.25.0" dependencies: - caniuse-lite: "npm:^1.0.30001716" - electron-to-chromium: "npm:^1.5.149" + caniuse-lite: "npm:^1.0.30001718" + electron-to-chromium: "npm:^1.5.160" node-releases: "npm:^2.0.19" update-browserslist-db: "npm:^1.1.3" bin: browserslist: cli.js - checksum: 10/93fde829b77f20e2c4e1e0eaed154681c05e4828420e4afba790d480daa5de742977a44bbac8567881b8fbec3da3dea7ca1cb578ac1fd4385ef4ae91ca691d64 + checksum: 10/4a5442b1a0d09c4c64454f184b8fed17d8c3e202034bf39de28f74497d7bd28dddee121b2bab4e34825fe0ed4c166d84e32a39f576c76fce73c1f8f05e4b6ee6 languageName: node linkType: hard @@ -6409,10 +6420,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001716, caniuse-lite@npm:^1.0.30001718": - version: 1.0.30001718 - resolution: "caniuse-lite@npm:1.0.30001718" - checksum: 10/e172a4c156f743cc947e659f353ad9edb045725cc109a02cc792dcbf98569356ebfa4bb4356e3febf87427aab0951c34c1ee5630629334f25ae6f76de7d86fd0 +"caniuse-lite@npm:^1.0.30001718": + version: 1.0.30001724 + resolution: "caniuse-lite@npm:1.0.30001724" + checksum: 10/0e95811e7c33410ec458784726b97f50f07fb0f6f17b2b17789bb2d5ba1ff126daa24549d698c0a8729f5236d98fde04bb44a3def22eb4667ac15bd80f20a4f2 languageName: node linkType: hard @@ -6887,11 +6898,11 @@ __metadata: linkType: hard "core-js-compat@npm:^3.40.0": - version: 3.42.0 - resolution: "core-js-compat@npm:3.42.0" + version: 3.43.0 + resolution: "core-js-compat@npm:3.43.0" dependencies: - browserslist: "npm:^4.24.4" - checksum: 10/2052c73e500e95420d948a0595f4055e40ca6a208cc15c7981b7f202efa851bfae3de59a13009dc367cc5fbaeb8ff84a64c7c0bfc37de4b3bd2cf6b0e14290bd + browserslist: "npm:^4.25.0" + checksum: 10/fa57a75e0e0798889f0a8d4dbc66bd276c799f265442eb0f6baa4113efaf0c4213e457c70f8f0f9d78f98b22c5c16dfd7e68d88e6f2484ae2120888a4bd08b68 languageName: node linkType: hard @@ -6959,12 +6970,12 @@ __metadata: linkType: hard "cssstyle@npm:^4.2.1": - version: 4.3.1 - resolution: "cssstyle@npm:4.3.1" + version: 4.5.0 + resolution: "cssstyle@npm:4.5.0" dependencies: - "@asamuzakjp/css-color": "npm:^3.1.2" + "@asamuzakjp/css-color": "npm:^3.2.0" rrweb-cssom: "npm:^0.8.0" - checksum: 10/e74b2636067c3fd912a16d8d979a7975e5a5c8b3ce9386298d75a82478bb6c8bc03b261b92575348f471b3eb7534d2594a0c4b47d6fc8c03605b2628dce992ff + checksum: 10/6bbf4ed5b8f8190389eca086018170e96250d86dad4d4e8955c67095999343085613e329fd6aac56f976c3f7e3c09862e17ed567b97e618d260e01be69267261 languageName: node linkType: hard @@ -7465,10 +7476,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.5.149": - version: 1.5.155 - resolution: "electron-to-chromium@npm:1.5.155" - checksum: 10/3c3fff052b8cdf18edca56b45ed1d52f665c943e6ef5c09cb33b2d06b774d705cc7679ea886bdde35b0ebafe30d6f570223fcfc7b687d7c0c34286368d7f4976 +"electron-to-chromium@npm:^1.5.160": + version: 1.5.171 + resolution: "electron-to-chromium@npm:1.5.171" + checksum: 10/6d58ff50407107d7e86e7beb8d0361358f90dbc10c7d92a2ff9cdfbaf27a65165c00ae05a345ab32fa6e371ff9c7d1fef1441d57adfa8f59701c56734745c0a1 languageName: node linkType: hard @@ -7524,11 +7535,11 @@ __metadata: linkType: hard "end-of-stream@npm:^1.4.4": - version: 1.4.4 - resolution: "end-of-stream@npm:1.4.4" + version: 1.4.5 + resolution: "end-of-stream@npm:1.4.5" dependencies: once: "npm:^1.4.0" - checksum: 10/530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b + checksum: 10/1e0cfa6e7f49887544e03314f9dfc56a8cb6dde910cbb445983ecc2ff426fc05946df9d75d8a21a3a64f2cecfe1bf88f773952029f46756b2ed64a24e95b1fb8 languageName: node linkType: hard @@ -7585,9 +7596,9 @@ __metadata: linkType: hard "entities@npm:^6.0.0": - version: 6.0.0 - resolution: "entities@npm:6.0.0" - checksum: 10/cf37a4aad887ba8573532346da1c78349dccd5b510a9bbddf92fe59b36b18a8b26fe619a862de4e7fd3b8addc6d5e0969261198bbeb690da87297011a61b7066 + version: 6.0.1 + resolution: "entities@npm:6.0.1" + checksum: 10/62af1307202884349d2867f0aac5c60d8b57102ea0b0e768b16246099512c28e239254ad772d6834e7e14cb1b6f153fc3d0c031934e3183b086c86d3838d874a languageName: node linkType: hard @@ -7630,26 +7641,26 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9": - version: 1.23.9 - resolution: "es-abstract@npm:1.23.9" +"es-abstract@npm:^1.23.2, es-abstract@npm:^1.23.5, es-abstract@npm:^1.23.6, es-abstract@npm:^1.23.9, es-abstract@npm:^1.24.0": + version: 1.24.0 + resolution: "es-abstract@npm:1.24.0" dependencies: array-buffer-byte-length: "npm:^1.0.2" arraybuffer.prototype.slice: "npm:^1.0.4" available-typed-arrays: "npm:^1.0.7" call-bind: "npm:^1.0.8" - call-bound: "npm:^1.0.3" + call-bound: "npm:^1.0.4" data-view-buffer: "npm:^1.0.2" data-view-byte-length: "npm:^1.0.2" data-view-byte-offset: "npm:^1.0.1" es-define-property: "npm:^1.0.1" es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.0.0" + es-object-atoms: "npm:^1.1.1" es-set-tostringtag: "npm:^2.1.0" es-to-primitive: "npm:^1.3.0" function.prototype.name: "npm:^1.1.8" - get-intrinsic: "npm:^1.2.7" - get-proto: "npm:^1.0.0" + get-intrinsic: "npm:^1.3.0" + get-proto: "npm:^1.0.1" get-symbol-description: "npm:^1.1.0" globalthis: "npm:^1.0.4" gopd: "npm:^1.2.0" @@ -7661,21 +7672,24 @@ __metadata: is-array-buffer: "npm:^3.0.5" is-callable: "npm:^1.2.7" is-data-view: "npm:^1.0.2" + is-negative-zero: "npm:^2.0.3" is-regex: "npm:^1.2.1" + is-set: "npm:^2.0.3" is-shared-array-buffer: "npm:^1.0.4" is-string: "npm:^1.1.1" is-typed-array: "npm:^1.1.15" - is-weakref: "npm:^1.1.0" + is-weakref: "npm:^1.1.1" math-intrinsics: "npm:^1.1.0" - object-inspect: "npm:^1.13.3" + object-inspect: "npm:^1.13.4" object-keys: "npm:^1.1.1" object.assign: "npm:^4.1.7" own-keys: "npm:^1.0.1" - regexp.prototype.flags: "npm:^1.5.3" + regexp.prototype.flags: "npm:^1.5.4" safe-array-concat: "npm:^1.1.3" safe-push-apply: "npm:^1.0.0" safe-regex-test: "npm:^1.1.0" set-proto: "npm:^1.0.0" + stop-iteration-iterator: "npm:^1.1.0" string.prototype.trim: "npm:^1.2.10" string.prototype.trimend: "npm:^1.0.9" string.prototype.trimstart: "npm:^1.0.8" @@ -7684,8 +7698,8 @@ __metadata: typed-array-byte-offset: "npm:^1.0.4" typed-array-length: "npm:^1.0.7" unbox-primitive: "npm:^1.1.0" - which-typed-array: "npm:^1.1.18" - checksum: 10/31a321966d760d88fc2ed984104841b42f4f24fc322b246002b9be0af162e03803ee41fcc3cf8be89e07a27ba3033168f877dd983703cb81422ffe5322a27582 + which-typed-array: "npm:^1.1.19" + checksum: 10/64e07a886f7439cf5ccfc100f9716e6173e10af6071a50a5031afbdde474a3dbc9619d5965da54e55f8908746a9134a46be02af8c732d574b7b81ed3124e2daf languageName: node linkType: hard @@ -7752,34 +7766,34 @@ __metadata: linkType: hard "esbuild@npm:^0.25.0": - version: 0.25.4 - resolution: "esbuild@npm:0.25.4" + version: 0.25.5 + resolution: "esbuild@npm:0.25.5" dependencies: - "@esbuild/aix-ppc64": "npm:0.25.4" - "@esbuild/android-arm": "npm:0.25.4" - "@esbuild/android-arm64": "npm:0.25.4" - "@esbuild/android-x64": "npm:0.25.4" - "@esbuild/darwin-arm64": "npm:0.25.4" - "@esbuild/darwin-x64": "npm:0.25.4" - "@esbuild/freebsd-arm64": "npm:0.25.4" - "@esbuild/freebsd-x64": "npm:0.25.4" - "@esbuild/linux-arm": "npm:0.25.4" - "@esbuild/linux-arm64": "npm:0.25.4" - "@esbuild/linux-ia32": "npm:0.25.4" - "@esbuild/linux-loong64": "npm:0.25.4" - "@esbuild/linux-mips64el": "npm:0.25.4" - "@esbuild/linux-ppc64": "npm:0.25.4" - "@esbuild/linux-riscv64": "npm:0.25.4" - "@esbuild/linux-s390x": "npm:0.25.4" - "@esbuild/linux-x64": "npm:0.25.4" - "@esbuild/netbsd-arm64": "npm:0.25.4" - "@esbuild/netbsd-x64": "npm:0.25.4" - "@esbuild/openbsd-arm64": "npm:0.25.4" - "@esbuild/openbsd-x64": "npm:0.25.4" - "@esbuild/sunos-x64": "npm:0.25.4" - "@esbuild/win32-arm64": "npm:0.25.4" - "@esbuild/win32-ia32": "npm:0.25.4" - "@esbuild/win32-x64": "npm:0.25.4" + "@esbuild/aix-ppc64": "npm:0.25.5" + "@esbuild/android-arm": "npm:0.25.5" + "@esbuild/android-arm64": "npm:0.25.5" + "@esbuild/android-x64": "npm:0.25.5" + "@esbuild/darwin-arm64": "npm:0.25.5" + "@esbuild/darwin-x64": "npm:0.25.5" + "@esbuild/freebsd-arm64": "npm:0.25.5" + "@esbuild/freebsd-x64": "npm:0.25.5" + "@esbuild/linux-arm": "npm:0.25.5" + "@esbuild/linux-arm64": "npm:0.25.5" + "@esbuild/linux-ia32": "npm:0.25.5" + "@esbuild/linux-loong64": "npm:0.25.5" + "@esbuild/linux-mips64el": "npm:0.25.5" + "@esbuild/linux-ppc64": "npm:0.25.5" + "@esbuild/linux-riscv64": "npm:0.25.5" + "@esbuild/linux-s390x": "npm:0.25.5" + "@esbuild/linux-x64": "npm:0.25.5" + "@esbuild/netbsd-arm64": "npm:0.25.5" + "@esbuild/netbsd-x64": "npm:0.25.5" + "@esbuild/openbsd-arm64": "npm:0.25.5" + "@esbuild/openbsd-x64": "npm:0.25.5" + "@esbuild/sunos-x64": "npm:0.25.5" + "@esbuild/win32-arm64": "npm:0.25.5" + "@esbuild/win32-ia32": "npm:0.25.5" + "@esbuild/win32-x64": "npm:0.25.5" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -7833,7 +7847,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/227ffe9b31f0b184a0b0a0210bb9d32b2b115b8c5c9b09f08db2c3928cb470fc55a22dbba3c2894365d3abcc62c2089b85638be96a20691d1234d31990ea01b2 + checksum: 10/0fa4c3b42c6ddf1a008e75a4bb3dcab08ce22ac0b31dd59dc01f7fe8e21380bfaec07a2fe3730a7cf430da5a30142d016714b358666325a4733547afa42be405 languageName: node linkType: hard @@ -7924,14 +7938,14 @@ __metadata: linkType: hard "eslint-module-utils@npm:^2.12.0": - version: 2.12.0 - resolution: "eslint-module-utils@npm:2.12.0" + version: 2.12.1 + resolution: "eslint-module-utils@npm:2.12.1" dependencies: debug: "npm:^3.2.7" peerDependenciesMeta: eslint: optional: true - checksum: 10/dd27791147eca17366afcb83f47d6825b6ce164abb256681e5de4ec1d7e87d8605641eb869298a0dbc70665e2446dbcc2f40d3e1631a9475dd64dd23d4ca5dee + checksum: 10/bd25d6610ec3abaa50e8f1beb0119541562bbb8dd02c035c7e887976fe1e0c5dd8175f4607ca8d86d1146df24d52a071bd3d1dd329f6902bd58df805a8ca16d3 languageName: node linkType: hard @@ -8423,15 +8437,15 @@ __metadata: languageName: node linkType: hard -"fdir@npm:^6.4.4": - version: 6.4.4 - resolution: "fdir@npm:6.4.4" +"fdir@npm:^6.4.4, fdir@npm:^6.4.6": + version: 6.4.6 + resolution: "fdir@npm:6.4.6" peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true - checksum: 10/d0000d6b790059b35f4ed19acc8847a66452e0bc68b28766c929ffd523e5ec2083811fc8a545e4a1d4945ce70e887b3a610c145c681073b506143ae3076342ed + checksum: 10/c186ba387e7b75ccf874a098d9bc5fe0af0e9c52fc56f8eac8e80aa4edb65532684bf2bf769894ff90f53bf221d6136692052d31f07a9952807acae6cbe7ee50 languageName: node linkType: hard @@ -8632,14 +8646,15 @@ __metadata: linkType: hard "form-data@npm:^4.0.0": - version: 4.0.2 - resolution: "form-data@npm:4.0.2" + version: 4.0.3 + resolution: "form-data@npm:4.0.3" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" es-set-tostringtag: "npm:^2.1.0" + hasown: "npm:^2.0.2" mime-types: "npm:^2.1.12" - checksum: 10/82c65b426af4a40090e517a1bc9057f76970b4c6043e37aa49859c447d88553e77d4cc5626395079a53d2b0889ba5f2a49f3900db3ad3f3f1bf76613532572fb + checksum: 10/22f6e55e6f32a5797a500ed7ca5aa9d690c4de6e1b3308f25f0d83a27d08d91a265ab59a190db2305b15144f8f07df08e8117bad6a93fc93de1baa838bfcc0b5 languageName: node linkType: hard @@ -9684,9 +9699,9 @@ __metadata: linkType: hard "ignore@npm:^7.0.0, ignore@npm:^7.0.3": - version: 7.0.4 - resolution: "ignore@npm:7.0.4" - checksum: 10/01ee59df2ffd14b0844efc17f5ab3642c848e45efdb7cc757928da5e076cb74313748f77f5ffe362a6407c5e7cc71f10fad5e8eb9d91c1a17c4e7ef2c1f8e40e + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: 10/f134b96a4de0af419196f52c529d5c6120c4456ff8a6b5a14ceaaa399f883e15d58d2ce651c9b69b9388491d4669dda47285d307e827de9304a53a1824801bc6 languageName: node linkType: hard @@ -10042,6 +10057,13 @@ __metadata: languageName: node linkType: hard +"is-negative-zero@npm:^2.0.3": + version: 2.0.3 + resolution: "is-negative-zero@npm:2.0.3" + checksum: 10/8fe5cffd8d4fb2ec7b49d657e1691889778d037494c6f40f4d1a524cadd658b4b53ad7b6b73a59bcb4b143ae9a3d15829af864b2c0f9d65ac1e678c4c80f17e5 + languageName: node + linkType: hard + "is-network-error@npm:^1.0.0": version: 1.1.0 resolution: "is-network-error@npm:1.1.0" @@ -10182,7 +10204,7 @@ __metadata: languageName: node linkType: hard -"is-string@npm:^1.0.7, is-string@npm:^1.1.1": +"is-string@npm:^1.1.1": version: 1.1.1 resolution: "is-string@npm:1.1.1" dependencies: @@ -10244,7 +10266,7 @@ __metadata: languageName: node linkType: hard -"is-weakref@npm:^1.0.2, is-weakref@npm:^1.1.0": +"is-weakref@npm:^1.0.2, is-weakref@npm:^1.1.1": version: 1.1.1 resolution: "is-weakref@npm:1.1.1" dependencies: @@ -11407,7 +11429,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.8": +"nanoid@npm:^3.3.11": version: 3.3.11 resolution: "nanoid@npm:3.3.11" bin: @@ -11596,7 +11618,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.13.3": +"object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": version: 1.13.4 resolution: "object-inspect@npm:1.13.4" checksum: 10/aa13b1190ad3e366f6c83ad8a16ed37a19ed57d267385aa4bfdccda833d7b90465c057ff6c55d035a6b2e52c1a2295582b294217a0a3a1ae7abdd6877ef781fb @@ -12198,14 +12220,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.14, postcss@npm:^8.5.3": - version: 8.5.3 - resolution: "postcss@npm:8.5.3" +"postcss@npm:^8.4.14, postcss@npm:^8.5.5": + version: 8.5.6 + resolution: "postcss@npm:8.5.6" dependencies: - nanoid: "npm:^3.3.8" + nanoid: "npm:^3.3.11" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10/6d7e21a772e8b05bf102636918654dac097bac013f0dc8346b72ac3604fc16829646f94ea862acccd8f82e910b00e2c11c1f0ea276543565d278c7ca35516a7c + checksum: 10/9e4fbe97574091e9736d0e82a591e29aa100a0bf60276a926308f8c57249698935f35c5d2f4e80de778d0cbb8dcffab4f383d85fd50c5649aca421c3df729b86 languageName: node linkType: hard @@ -12518,7 +12540,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.3": +"regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4": version: 1.5.4 resolution: "regexp.prototype.flags@npm:1.5.4" dependencies: @@ -12784,30 +12806,30 @@ __metadata: linkType: hard "rollup@npm:^4.40.0": - version: 4.42.0 - resolution: "rollup@npm:4.42.0" + version: 4.44.0 + resolution: "rollup@npm:4.44.0" dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.42.0" - "@rollup/rollup-android-arm64": "npm:4.42.0" - "@rollup/rollup-darwin-arm64": "npm:4.42.0" - "@rollup/rollup-darwin-x64": "npm:4.42.0" - "@rollup/rollup-freebsd-arm64": "npm:4.42.0" - "@rollup/rollup-freebsd-x64": "npm:4.42.0" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.42.0" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.42.0" - "@rollup/rollup-linux-arm64-gnu": "npm:4.42.0" - "@rollup/rollup-linux-arm64-musl": "npm:4.42.0" - "@rollup/rollup-linux-loongarch64-gnu": "npm:4.42.0" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.42.0" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.42.0" - "@rollup/rollup-linux-riscv64-musl": "npm:4.42.0" - "@rollup/rollup-linux-s390x-gnu": "npm:4.42.0" - "@rollup/rollup-linux-x64-gnu": "npm:4.42.0" - "@rollup/rollup-linux-x64-musl": "npm:4.42.0" - "@rollup/rollup-win32-arm64-msvc": "npm:4.42.0" - "@rollup/rollup-win32-ia32-msvc": "npm:4.42.0" - "@rollup/rollup-win32-x64-msvc": "npm:4.42.0" - "@types/estree": "npm:1.0.7" + "@rollup/rollup-android-arm-eabi": "npm:4.44.0" + "@rollup/rollup-android-arm64": "npm:4.44.0" + "@rollup/rollup-darwin-arm64": "npm:4.44.0" + "@rollup/rollup-darwin-x64": "npm:4.44.0" + "@rollup/rollup-freebsd-arm64": "npm:4.44.0" + "@rollup/rollup-freebsd-x64": "npm:4.44.0" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.44.0" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.44.0" + "@rollup/rollup-linux-arm64-gnu": "npm:4.44.0" + "@rollup/rollup-linux-arm64-musl": "npm:4.44.0" + "@rollup/rollup-linux-loongarch64-gnu": "npm:4.44.0" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.44.0" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.44.0" + "@rollup/rollup-linux-riscv64-musl": "npm:4.44.0" + "@rollup/rollup-linux-s390x-gnu": "npm:4.44.0" + "@rollup/rollup-linux-x64-gnu": "npm:4.44.0" + "@rollup/rollup-linux-x64-musl": "npm:4.44.0" + "@rollup/rollup-win32-arm64-msvc": "npm:4.44.0" + "@rollup/rollup-win32-ia32-msvc": "npm:4.44.0" + "@rollup/rollup-win32-x64-msvc": "npm:4.44.0" + "@types/estree": "npm:1.0.8" fsevents: "npm:~2.3.2" dependenciesMeta: "@rollup/rollup-android-arm-eabi": @@ -12854,7 +12876,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 10/167697d97c3a14850c04a045462056518a2b73dd5b2cb61e9e98fb7197373a5966464557d35344439119ce4d2bcb3d6a955184e950523a57daae5cc08fb7e0f2 + checksum: 10/2182fc751734277972c011bf62a07cd01de44aaa408f29d3be51b6c7373aa179c9e20d5b9b9fa46268c7d65fc8edb033243f501495465b13dd05d1f99635a7fa languageName: node linkType: hard @@ -12875,9 +12897,9 @@ __metadata: linkType: hard "rslog@npm:^1.2.3": - version: 1.2.3 - resolution: "rslog@npm:1.2.3" - checksum: 10/b655304394dba95b83e3b932c3788a5a9f408c113a25b5fd08950904f1f80476fc049c67744bc427837d47dfb1fc0a9a0b48cfd7c21f536bb6cb8d86d46f90e8 + version: 1.2.7 + resolution: "rslog@npm:1.2.7" + checksum: 10/941fc2430c7cee95befe3ecb493aae51321cf6a7e0d58b31be751860fbe321eb198fa48a1d880ab0b58e9c57f36f0cdd98550d72b8405282ef74a4193e330521 languageName: node linkType: hard @@ -13215,9 +13237,9 @@ __metadata: linkType: hard "shell-quote@npm:^1.8.1": - version: 1.8.2 - resolution: "shell-quote@npm:1.8.2" - checksum: 10/3ae4804fd80a12ba07650d0262804ae3b479a62a6b6971a6dc5fa12995507aa63d3de3e6a8b7a8d18f4ce6eb118b7d75db7fcb2c0acbf016f210f746b10cfe02 + version: 1.8.3 + resolution: "shell-quote@npm:1.8.3" + checksum: 10/5473e354637c2bd698911224129c9a8961697486cff1fb221f234d71c153fc377674029b0223d1d3c953a68d451d79366abfe53d1a0b46ee1f28eb9ade928f4c languageName: node linkType: hard @@ -13413,12 +13435,12 @@ __metadata: linkType: hard "socks@npm:^2.8.3": - version: 2.8.4 - resolution: "socks@npm:2.8.4" + version: 2.8.5 + resolution: "socks@npm:2.8.5" dependencies: ip-address: "npm:^9.0.5" smart-buffer: "npm:^4.2.0" - checksum: 10/ab3af97aeb162f32c80e176c717ccf16a11a6ebb4656a62b94c0f96495ea2a1f4a8206c04b54438558485d83d0c5f61920c07a1a5d3963892a589b40cc6107dd + checksum: 10/0109090ec2bcb8d12d3875a987e85539ed08697500ad971a603c3057e4c266b4bf6a603e07af6d19218c422dd9b72d923aaa6c1f20abae275510bba458e4ccc9 languageName: node linkType: hard @@ -13605,6 +13627,16 @@ __metadata: languageName: node linkType: hard +"stop-iteration-iterator@npm:^1.1.0": + version: 1.1.0 + resolution: "stop-iteration-iterator@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + internal-slot: "npm:^1.1.0" + checksum: 10/ff36c4db171ee76c936ccfe9541946b77017f12703d4c446652017356816862d3aa029a64e7d4c4ceb484e00ed4a81789333896390d808458638f3a216aa1f41 + languageName: node + linkType: hard + "stream-composer@npm:^1.0.2": version: 1.0.2 resolution: "stream-composer@npm:1.0.2" @@ -13631,8 +13663,8 @@ __metadata: linkType: hard "streamx@npm:^2.12.0, streamx@npm:^2.12.5, streamx@npm:^2.13.2, streamx@npm:^2.14.0": - version: 2.22.0 - resolution: "streamx@npm:2.22.0" + version: 2.22.1 + resolution: "streamx@npm:2.22.1" dependencies: bare-events: "npm:^2.2.0" fast-fifo: "npm:^1.3.2" @@ -13640,7 +13672,7 @@ __metadata: dependenciesMeta: bare-events: optional: true - checksum: 10/9c329bb316e2085e207e471ecd0da18b4ed5b1cfe5cf10e9e7fad3f8f50c6ca1a6a844bdfd9bc7521560b97f229890de82ca162a0e66115300b91a489b1cbefd + checksum: 10/6d8576e0e5f4a67776427e3d29a877e66295bf7e17019a5b5c77d7fa026c5e8df6cdbd0cec2774999075af985179d70f07b25db7557b9226e33148fe67edd487 languageName: node linkType: hard @@ -14010,8 +14042,8 @@ __metadata: linkType: hard "terser@npm:^5.15.1, terser@npm:^5.17.4, terser@npm:^5.31.1": - version: 5.39.2 - resolution: "terser@npm:5.39.2" + version: 5.43.1 + resolution: "terser@npm:5.43.1" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.14.0" @@ -14019,7 +14051,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10/07fd3533858c42f01e942964818727cdeba59c3d49431066b68d640b57f7e854e5c473dc77a4ef38378ad288a8fa0c322bf56801fee6651b1fe4dc9d2b6869f0 + checksum: 10/c0a0fd62319e0ce66e800f57ae12ef4ca45f12e9422dac160b866f0d890d01f8b547c96de2557b8443d96953db36be5d900e8006436ef9f628dbd38082e8fe5d languageName: node linkType: hard @@ -14256,11 +14288,11 @@ __metadata: linkType: hard "tree-dump@npm:^1.0.1": - version: 1.0.2 - resolution: "tree-dump@npm:1.0.2" + version: 1.0.3 + resolution: "tree-dump@npm:1.0.3" peerDependencies: tslib: 2 - checksum: 10/ddcde4da9ded8edc2fa77fc9153ef8d7fba9cd5f813db27c30c7039191b50e1512b7106f0f4fe7ccaa3aa69f85b4671eda7ed0b9f9d34781eb26ebe4593ad4eb + checksum: 10/cf382e61cfb5e3ff8f03425b5bc1923e8f0e385b3a02f43d9d0a32d09da9984477e0f2a7698628662263d1d3f1af17e33486c77ff454978f0f9f07fb5d1fe9a2 languageName: node linkType: hard @@ -14291,8 +14323,8 @@ __metadata: linkType: hard "tsconfck@npm:^3.0.3": - version: 3.1.5 - resolution: "tsconfck@npm:3.1.5" + version: 3.1.6 + resolution: "tsconfck@npm:3.1.6" peerDependencies: typescript: ^5.0.0 peerDependenciesMeta: @@ -14300,7 +14332,7 @@ __metadata: optional: true bin: tsconfck: bin/tsconfck.js - checksum: 10/46b68f0fcec7da045490e427400c2a7fea67bdb6dae871257e8d2ec48e9dc99674214df86ff51c6d01ceb68ba9d7d806d3b69de432efa3c76b5fba160c252857 + checksum: 10/8574595286850273bf83319b4e67ca760088df3c36f7ca1425aaf797416672e854271bd31e75c9b3e1836ed5b66410c6bc38cbbda9c638a5416c6a682ed94132 languageName: node linkType: hard @@ -14558,10 +14590,10 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.21.0": - version: 6.21.0 - resolution: "undici-types@npm:6.21.0" - checksum: 10/ec8f41aa4359d50f9b59fa61fe3efce3477cc681908c8f84354d8567bb3701fafdddf36ef6bff307024d3feb42c837cf6f670314ba37fc8145e219560e473d14 +"undici-types@npm:~7.8.0": + version: 7.8.0 + resolution: "undici-types@npm:7.8.0" + checksum: 10/fcff3fbab234f067fbd69e374ee2c198ba74c364ceaf6d93db7ca267e784457b5518cd01d0d2329b075f412574205ea3172a9a675facb49b4c9efb7141cd80b7 languageName: node linkType: hard @@ -14858,14 +14890,14 @@ __metadata: linkType: hard "vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": - version: 7.0.0-beta.0 - resolution: "vite@npm:7.0.0-beta.0" + version: 7.0.0-beta.2 + resolution: "vite@npm:7.0.0-beta.2" dependencies: esbuild: "npm:^0.25.0" - fdir: "npm:^6.4.4" + fdir: "npm:^6.4.6" fsevents: "npm:~2.3.3" picomatch: "npm:^4.0.2" - postcss: "npm:^8.5.3" + postcss: "npm:^8.5.5" rollup: "npm:^4.40.0" tinyglobby: "npm:^0.2.14" peerDependencies: @@ -14908,7 +14940,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10/87a5837832f9b6ca873c1808fd57e031d9ddfdea788b24bc7d137316af6dcf842ec4d81701d24b7e3bb6eef33a7b718c1aa33a1304c952f3e3eab9e8498b60b0 + checksum: 10/01245969939849d2a1fbfc6bba95b80079ecaf2a181bf530a35718bc8e093b49f92c0d228e64e7cf8d1976fdf77da5ca4ff0fd8d8e1df6bd81830c51c79e3b98 languageName: node linkType: hard @@ -15353,7 +15385,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18": +"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19": version: 1.1.19 resolution: "which-typed-array@npm:1.1.19" dependencies: From ad589b32c91262f25b05bad781c2bed7aa155e23 Mon Sep 17 00:00:00 2001 From: iluvdata <86066778+iluvdata@users.noreply.github.com> Date: Sat, 21 Jun 2025 04:09:13 -0400 Subject: [PATCH 076/216] allow previews in config_subentries_flow (#25859) --- src/data/preview.ts | 2 +- src/dialogs/config-flow/previews/flow-preview-generic.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/data/preview.ts b/src/data/preview.ts index fb009f3981..dadbd8d859 100644 --- a/src/data/preview.ts +++ b/src/data/preview.ts @@ -13,7 +13,7 @@ export const subscribePreviewGeneric = ( hass: HomeAssistant, domain: string, flow_id: string, - flow_type: "config_flow" | "options_flow", + flow_type: "config_flow" | "options_flow" | "config_subentries_flow", user_input: Record, callback: (preview: GenericPreview) => void ): Promise => diff --git a/src/dialogs/config-flow/previews/flow-preview-generic.ts b/src/dialogs/config-flow/previews/flow-preview-generic.ts index 2fcd270875..c95f330df6 100644 --- a/src/dialogs/config-flow/previews/flow-preview-generic.ts +++ b/src/dialogs/config-flow/previews/flow-preview-generic.ts @@ -82,7 +82,11 @@ export class FlowPreviewGeneric extends LitElement { (await this._unsub)(); this._unsub = undefined; } - if (this.flowType !== "config_flow" && this.flowType !== "options_flow") { + if ( + this.flowType !== "config_flow" && + this.flowType !== "options_flow" && + this.flowType !== "config_subentries_flow" + ) { return; } this._error = undefined; From f50d5d79a43e2cfb2b84dc57f6017ce70f7ee568 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Sun, 22 Jun 2025 13:11:29 +0300 Subject: [PATCH 077/216] Another fix for history chart axis rounding (#25852) --- .../chart/state-history-chart-line.ts | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index f8f7689539..10ba4dcb50 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -729,20 +729,17 @@ export class StateHistoryChartLine extends LitElement { } private _formatYAxisLabel = (value: number) => { - const formatOptions = - value >= 1 || value <= -1 - ? undefined - : { - // show the first significant digit for tiny values - maximumFractionDigits: Math.max( - 2, - // use the difference to the previous value to determine the number of significant digits #25526 - -Math.floor( - Math.log10(Math.abs(value - this._previousYAxisLabelValue || 1)) - ) - ), - }; - const label = formatNumber(value, this.hass.locale, formatOptions); + // show the first significant digit for tiny values + const maximumFractionDigits = Math.max( + 1, + // use the difference to the previous value to determine the number of significant digits #25526 + -Math.floor( + Math.log10(Math.abs(value - this._previousYAxisLabelValue || 1)) + ) + ); + const label = formatNumber(value, this.hass.locale, { + maximumFractionDigits, + }); const width = measureTextWidth(label, 12) + 5; if (width > this._yWidth) { this._yWidth = width; From fdd6ccf379135b8bacc924bb53b5f55f1656daf7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 18:48:32 +0300 Subject: [PATCH 078/216] Update dependency @rsdoctor/rspack-plugin to v1.1.4 (#25868) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 102 +++++++++++++++++++++++++-------------------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index ecd64bd56c..a92ec0f7bc 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,7 @@ "@octokit/auth-oauth-device": "8.0.1", "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", - "@rsdoctor/rspack-plugin": "1.1.3", + "@rsdoctor/rspack-plugin": "1.1.4", "@rspack/cli": "1.3.12", "@rspack/core": "1.3.12", "@types/babel__plugin-transform-runtime": "7.9.5", diff --git a/yarn.lock b/yarn.lock index 7c16f1005b..fb26be3a89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3765,76 +3765,76 @@ __metadata: languageName: node linkType: hard -"@rsdoctor/client@npm:1.1.3": - version: 1.1.3 - resolution: "@rsdoctor/client@npm:1.1.3" - checksum: 10/bb2ce31c39427e090d07dfd525323f666ca6ba7bcf849ce022f40c4141711858599bbe1a3f05ce8a4836e2d0a862d18c02dc5d92222e5698ba4c0c44265e22e6 +"@rsdoctor/client@npm:1.1.4": + version: 1.1.4 + resolution: "@rsdoctor/client@npm:1.1.4" + checksum: 10/549dd4093f2c652522351aafa7be36e565b51c0f4c5fbabb2d58c79f74554399059f71cf543cb82cf9af77b195a80dbed4609c3a384f46f597b6bbaaa75433a2 languageName: node linkType: hard -"@rsdoctor/core@npm:1.1.3": - version: 1.1.3 - resolution: "@rsdoctor/core@npm:1.1.3" +"@rsdoctor/core@npm:1.1.4": + version: 1.1.4 + resolution: "@rsdoctor/core@npm:1.1.4" dependencies: "@rsbuild/plugin-check-syntax": "npm:1.3.0" - "@rsdoctor/graph": "npm:1.1.3" - "@rsdoctor/sdk": "npm:1.1.3" - "@rsdoctor/types": "npm:1.1.3" - "@rsdoctor/utils": "npm:1.1.3" - axios: "npm:^1.9.0" + "@rsdoctor/graph": "npm:1.1.4" + "@rsdoctor/sdk": "npm:1.1.4" + "@rsdoctor/types": "npm:1.1.4" + "@rsdoctor/utils": "npm:1.1.4" + axios: "npm:^1.10.0" browserslist-load-config: "npm:^1.0.0" enhanced-resolve: "npm:5.12.0" filesize: "npm:^10.1.6" fs-extra: "npm:^11.1.1" lodash: "npm:^4.17.21" path-browserify: "npm:1.0.1" - semver: "npm:^7.6.3" + semver: "npm:^7.7.2" source-map: "npm:^0.7.4" webpack-bundle-analyzer: "npm:^4.10.2" - checksum: 10/6e742f47e147f4355fc76dbc4bcfa1b674055d60bf09f6c2569f60207cdbe36f513a8ede5522c15238aaa15328441edc74256011f8185cf1f297504847bf293b + checksum: 10/bbba4429dffe55ad7ec6df10aab5c2b895a6dd1d95b619a7e1e5f1d66f09127098618bce12aa959cc5e4bfcd670171aa7e472109deb47afce39814d69229bfe6 languageName: node linkType: hard -"@rsdoctor/graph@npm:1.1.3": - version: 1.1.3 - resolution: "@rsdoctor/graph@npm:1.1.3" +"@rsdoctor/graph@npm:1.1.4": + version: 1.1.4 + resolution: "@rsdoctor/graph@npm:1.1.4" dependencies: - "@rsdoctor/types": "npm:1.1.3" - "@rsdoctor/utils": "npm:1.1.3" + "@rsdoctor/types": "npm:1.1.4" + "@rsdoctor/utils": "npm:1.1.4" lodash.unionby: "npm:^4.8.0" socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" - checksum: 10/e948d2d106d0411ba43c25b4c57c9fd602795a64d2b36c6be61dd2cccfe65c4e256785f377586e9ff955631b55959ff5faa137f3f0097aa175125f9bc1e901f6 + checksum: 10/6f067e602d223a6743a3d858cf25a9967d8ccd572980a0841a6fb00789110c7ddca64548213dfb7848158579b6c16f31a700f93fecb90f0d6d399951e045bf77 languageName: node linkType: hard -"@rsdoctor/rspack-plugin@npm:1.1.3": - version: 1.1.3 - resolution: "@rsdoctor/rspack-plugin@npm:1.1.3" +"@rsdoctor/rspack-plugin@npm:1.1.4": + version: 1.1.4 + resolution: "@rsdoctor/rspack-plugin@npm:1.1.4" dependencies: - "@rsdoctor/core": "npm:1.1.3" - "@rsdoctor/graph": "npm:1.1.3" - "@rsdoctor/sdk": "npm:1.1.3" - "@rsdoctor/types": "npm:1.1.3" - "@rsdoctor/utils": "npm:1.1.3" + "@rsdoctor/core": "npm:1.1.4" + "@rsdoctor/graph": "npm:1.1.4" + "@rsdoctor/sdk": "npm:1.1.4" + "@rsdoctor/types": "npm:1.1.4" + "@rsdoctor/utils": "npm:1.1.4" lodash: "npm:^4.17.21" peerDependencies: "@rspack/core": "*" peerDependenciesMeta: "@rspack/core": optional: true - checksum: 10/c5afa5b60d66aa81ee30938f251b8ef10765c5413346b9c16367c4afe709e6fea05dffb5642ff0c1e5dc2ec2a7e27c58f3aa02f1a3b44a5fbca216460faa2e56 + checksum: 10/c6a58e6321674c87a3f9e56cd57c21a9342139c43cae918730f6cf375dcfbd4cad3f458add3604169fcbee96e623eb5bc4c7d661c720a653d41968d860b7dc25 languageName: node linkType: hard -"@rsdoctor/sdk@npm:1.1.3": - version: 1.1.3 - resolution: "@rsdoctor/sdk@npm:1.1.3" +"@rsdoctor/sdk@npm:1.1.4": + version: 1.1.4 + resolution: "@rsdoctor/sdk@npm:1.1.4" dependencies: - "@rsdoctor/client": "npm:1.1.3" - "@rsdoctor/graph": "npm:1.1.3" - "@rsdoctor/types": "npm:1.1.3" - "@rsdoctor/utils": "npm:1.1.3" + "@rsdoctor/client": "npm:1.1.4" + "@rsdoctor/graph": "npm:1.1.4" + "@rsdoctor/types": "npm:1.1.4" + "@rsdoctor/utils": "npm:1.1.4" "@types/fs-extra": "npm:^11.0.4" body-parser: "npm:1.20.3" cors: "npm:2.8.5" @@ -3847,13 +3847,13 @@ __metadata: socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" tapable: "npm:2.2.2" - checksum: 10/fdf4ad02e2be6f917c85d7912870d0ad606e77b194a7e6a75fd72942e5fce6ac04fada761d2bf010da50e38473439a7c246344b8db3511ade9925c8a1c6f4950 + checksum: 10/856df8c95e5329c52d90aa7a311e2b34252e6268a13476e887f2939d67dbbacb8aadbbe2bbaba46c43009345c0eaf490afc1e57f0570aa0af370ba6a3362098a languageName: node linkType: hard -"@rsdoctor/types@npm:1.1.3": - version: 1.1.3 - resolution: "@rsdoctor/types@npm:1.1.3" +"@rsdoctor/types@npm:1.1.4": + version: 1.1.4 + resolution: "@rsdoctor/types@npm:1.1.4" dependencies: "@types/connect": "npm:3.4.38" "@types/estree": "npm:1.0.5" @@ -3867,16 +3867,16 @@ __metadata: optional: true webpack: optional: true - checksum: 10/6b3984587b689f4004bb3966396341e00ad4510bd767bbcbeea19e48584bad1d925236e507ec465678093aea702541431594b140c9403c7f0808e48065433b2b + checksum: 10/f9d9c63b042aa9272a4b7d50500c699c3246a1c4937a6f0fb71b8d8911da71f08a472e2b3bd112b47248e2d05ce58af7326d07ab57e66ea7b07aa7b1d93f3761 languageName: node linkType: hard -"@rsdoctor/utils@npm:1.1.3": - version: 1.1.3 - resolution: "@rsdoctor/utils@npm:1.1.3" +"@rsdoctor/utils@npm:1.1.4": + version: 1.1.4 + resolution: "@rsdoctor/utils@npm:1.1.4" dependencies: "@babel/code-frame": "npm:7.26.2" - "@rsdoctor/types": "npm:1.1.3" + "@rsdoctor/types": "npm:1.1.4" "@types/estree": "npm:1.0.5" acorn: "npm:^8.10.0" acorn-import-attributes: "npm:^1.9.5" @@ -3890,9 +3890,9 @@ __metadata: json-stream-stringify: "npm:3.0.1" lines-and-columns: "npm:2.0.4" picocolors: "npm:^1.1.1" - rslog: "npm:^1.2.3" + rslog: "npm:^1.2.4" strip-ansi: "npm:^6.0.1" - checksum: 10/ee6c5529b66f62dbd2e47948b5836e742032d02cfea33d7f50db4d3a411e740cee38b19214824b3a5b3360f62bdd260b408d39d1ca55a008c854d026fbdb9f3d + checksum: 10/355e48cf9973d66b38666192af333a9c3f0abbe06c3a9872830070a46aacabbcf48321f91c4095832cbfea263c522f2c0054511eca396a50272d30c14302787e languageName: node linkType: hard @@ -5973,7 +5973,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.9.0": +"axios@npm:^1.10.0": version: 1.10.0 resolution: "axios@npm:1.10.0" dependencies: @@ -9312,7 +9312,7 @@ __metadata: "@octokit/plugin-retry": "npm:8.0.1" "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" - "@rsdoctor/rspack-plugin": "npm:1.1.3" + "@rsdoctor/rspack-plugin": "npm:1.1.4" "@rspack/cli": "npm:1.3.12" "@rspack/core": "npm:1.3.12" "@shoelace-style/shoelace": "npm:2.20.1" @@ -12896,7 +12896,7 @@ __metadata: languageName: node linkType: hard -"rslog@npm:^1.2.3": +"rslog@npm:^1.2.4": version: 1.2.7 resolution: "rslog@npm:1.2.7" checksum: 10/941fc2430c7cee95befe3ecb493aae51321cf6a7e0d58b31be751860fbe321eb198fa48a1d880ab0b58e9c57f36f0cdd98550d72b8405282ef74a4193e330521 @@ -13053,7 +13053,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.6.0, semver@npm:^7.6.3": +"semver@npm:^7.3.5, semver@npm:^7.5.3, semver@npm:^7.6.0, semver@npm:^7.7.2": version: 7.7.2 resolution: "semver@npm:7.7.2" bin: From 589fa75b17631bdf22ecf53eee27b3bb5d5f0443 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 22 Jun 2025 14:02:39 -0400 Subject: [PATCH 079/216] Add support for accept keyword in media selector (#25808) --- .../ha-selector/ha-selector-media.ts | 161 ++++++++++-------- .../ha-selector/ha-selector-selector.ts | 11 +- .../dialog-media-player-browse.ts | 1 + .../media-player/ha-media-player-browse.ts | 48 +++++- .../media-player/show-media-browser-dialog.ts | 3 +- src/data/selector.ts | 4 +- 6 files changed, 150 insertions(+), 78 deletions(-) diff --git a/src/components/ha-selector/ha-selector-media.ts b/src/components/ha-selector/ha-selector-media.ts index 11a55d001d..def2f3d9bc 100644 --- a/src/components/ha-selector/ha-selector-media.ts +++ b/src/components/ha-selector/ha-selector-media.ts @@ -1,6 +1,6 @@ import { mdiPlayBox, mdiPlus } from "@mdi/js"; import type { PropertyValues } from "lit"; -import { css, html, LitElement } from "lit"; +import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../../common/dom/fire_event"; @@ -84,20 +84,30 @@ export class HaMediaSelector extends LitElement { (stateObj && supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)); - return html` + const hasAccept = this.selector.media?.accept?.length; + + return html` + ${hasAccept + ? nothing + : html` + + `} ${!supportsBrowse - ? html` + ? html` + ${this.hass.localize( "ui.components.selectors.media.browse_not_supported" )} @@ -107,62 +117,72 @@ export class HaMediaSelector extends LitElement { .data=${this.value} .schema=${MANUAL_SCHEMA} .computeLabel=${this._computeLabelCallback} - >` - : html` -
    + ` + : html` + - ${this.value?.metadata?.thumbnail - ? html` -
    + ${this.value?.metadata?.thumbnail + ? html` +
    - ` - : html` -
    - -
    - `} -
    -
    - ${!this.value?.media_content_id - ? this.hass.localize("ui.components.selectors.media.pick_media") - : this.value.metadata?.title || this.value.media_content_id} -
    -
    `}`; + style=${this._thumbnailUrl + ? `background-image: url(${this._thumbnailUrl});` + : ""} + >
    + ` + : html` +
    + +
    + `} +
    +
    + ${!this.value?.media_content_id + ? this.hass.localize( + "ui.components.selectors.media.pick_media" + ) + : this.value.metadata?.title || this.value.media_content_id} +
    +
    + `} + `; } private _computeLabelCallback = ( @@ -184,8 +204,9 @@ export class HaMediaSelector extends LitElement { private _pickMedia() { showMediaBrowserDialog(this, { action: "pick", - entityId: this.value!.entity_id!, - navigateIds: this.value!.metadata?.navigateIds, + entityId: this.value?.entity_id, + navigateIds: this.value?.metadata?.navigateIds, + accept: this.selector.media?.accept, mediaPickedCallback: (pickedMedia: MediaPickedEvent) => { fireEvent(this, "value-changed", { value: { diff --git a/src/components/ha-selector/ha-selector-selector.ts b/src/components/ha-selector/ha-selector-selector.ts index af38d908d6..0cb50aa39a 100644 --- a/src/components/ha-selector/ha-selector-selector.ts +++ b/src/components/ha-selector/ha-selector-selector.ts @@ -80,7 +80,16 @@ const SELECTOR_SCHEMAS = { ] as const, icon: [] as const, location: [] as const, - media: [] as const, + media: [ + { + name: "accept", + selector: { + text: { + multiple: true, + }, + }, + }, + ] as const, number: [ { name: "min", diff --git a/src/components/media-player/dialog-media-player-browse.ts b/src/components/media-player/dialog-media-player-browse.ts index 7210065897..1254579492 100644 --- a/src/components/media-player/dialog-media-player-browse.ts +++ b/src/components/media-player/dialog-media-player-browse.ts @@ -164,6 +164,7 @@ class DialogMediaPlayerBrowse extends LitElement { .navigateIds=${this._navigateIds} .action=${this._action} .preferredLayout=${this._preferredLayout} + .accept=${this._params.accept} @close-dialog=${this.closeDialog} @media-picked=${this._mediaPicked} @media-browsed=${this._mediaBrowsed} diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index aa7e8ad8b0..0a0d6fb3f1 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -78,7 +78,7 @@ export interface MediaPlayerItemId { export class HaMediaPlayerBrowse extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public entityId!: string; + @property({ attribute: false }) public entityId?: string; @property() public action: MediaPlayerBrowseAction = "play"; @@ -89,6 +89,8 @@ export class HaMediaPlayerBrowse extends LitElement { @property({ attribute: false }) public navigateIds: MediaPlayerItemId[] = []; + @property({ attribute: false }) public accept?: string[]; + // @todo Consider reworking to eliminate need for attribute since it is manipulated internally @property({ type: Boolean, reflect: true }) public narrow = false; @@ -250,6 +252,7 @@ export class HaMediaPlayerBrowse extends LitElement { }); } else if ( err.code === "entity_not_found" && + this.entityId && isUnavailableState(this.hass.states[this.entityId]?.state) ) { this._setError({ @@ -334,7 +337,37 @@ export class HaMediaPlayerBrowse extends LitElement { const subtitle = this.hass.localize( `ui.components.media-browser.class.${currentItem.media_class}` ); - const children = currentItem.children || []; + let children = currentItem.children || []; + const canPlayChildren = new Set(); + + // Filter children based on accept property if provided + if (this.accept && children.length > 0) { + let checks: ((t: string) => boolean)[] = []; + + for (const type of this.accept) { + if (type.endsWith("/*")) { + const baseType = type.slice(0, -1); + checks.push((t) => t.startsWith(baseType)); + } else if (type === "*") { + checks = [() => true]; + break; + } else { + checks.push((t) => t === type); + } + } + + children = children.filter((child) => { + const contentType = child.media_content_type.toLowerCase(); + const canPlay = + child.media_content_type && + checks.some((check) => check(contentType)); + if (canPlay) { + canPlayChildren.add(child.media_content_id); + } + return !child.media_content_type || child.can_expand || canPlay; + }); + } + const mediaClass = MediaClassBrowserSettings[currentItem.media_class]; const childrenMediaClass = currentItem.children_media_class ? MediaClassBrowserSettings[currentItem.children_media_class] @@ -367,7 +400,12 @@ export class HaMediaPlayerBrowse extends LitElement { "" )}" > - ${this.narrow && currentItem?.can_play + ${this.narrow && + currentItem?.can_play && + (!this.accept || + canPlayChildren.has( + currentItem.media_content_id + )) ? html` { - return entityId !== BROWSER_PLAYER + return entityId && entityId !== BROWSER_PLAYER ? browseMediaPlayer(this.hass, entityId, mediaContentId, mediaContentType) : browseLocalMediaPlayer(this.hass, mediaContentId); } diff --git a/src/components/media-player/show-media-browser-dialog.ts b/src/components/media-player/show-media-browser-dialog.ts index 9136be8db0..5fdfeac789 100644 --- a/src/components/media-player/show-media-browser-dialog.ts +++ b/src/components/media-player/show-media-browser-dialog.ts @@ -7,10 +7,11 @@ import type { MediaPlayerItemId } from "./ha-media-player-browse"; export interface MediaPlayerBrowseDialogParams { action: MediaPlayerBrowseAction; - entityId: string; + entityId?: string; mediaPickedCallback: (pickedMedia: MediaPickedEvent) => void; navigateIds?: MediaPlayerItemId[]; minimumNavigateLevel?: number; + accept?: string[]; } export const showMediaBrowserDialog = ( diff --git a/src/data/selector.ts b/src/data/selector.ts index c76a458a05..abac290475 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -303,7 +303,9 @@ export interface LocationSelectorValue { } export interface MediaSelector { - media: {} | null; + media: { + accept?: string[]; + } | null; } export interface MediaSelectorValue { From f6f2cb0fce6e6ffb36b421b2e24a9118c6aa9d90 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 23 Jun 2025 08:42:54 +0300 Subject: [PATCH 080/216] Round chart limits with fit_y_data (#25851) --- src/components/chart/state-history-chart-line.ts | 14 ++++++++++---- src/components/chart/statistics-chart.ts | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index 10ba4dcb50..362d7ab579 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -226,22 +226,24 @@ export class StateHistoryChartLine extends LitElement { this.maxYAxis; if (typeof minYAxis === "number") { if (this.fitYData) { - minYAxis = ({ min }) => Math.min(min, this.minYAxis!); + minYAxis = ({ min }) => + Math.min(this._roundYAxis(min, Math.floor), this.minYAxis!); } } else if (this.logarithmicScale) { minYAxis = ({ min }) => { const value = min > 0 ? min * 0.95 : min * 1.05; - return Math.abs(value) < 1 ? value : Math.floor(value); + return this._roundYAxis(value, Math.floor); }; } if (typeof maxYAxis === "number") { if (this.fitYData) { - maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!); + maxYAxis = ({ max }) => + Math.max(this._roundYAxis(max, Math.ceil), this.maxYAxis!); } } else if (this.logarithmicScale) { maxYAxis = ({ max }) => { const value = max > 0 ? max * 1.05 : max * 0.95; - return Math.abs(value) < 1 ? value : Math.ceil(value); + return this._roundYAxis(value, Math.ceil); }; } this._chartOptions = { @@ -764,6 +766,10 @@ export class StateHistoryChartLine extends LitElement { } return value; } + + private _roundYAxis(value: number, roundingFn: (value: number) => number) { + return Math.abs(value) < 1 ? value : roundingFn(value); + } } customElements.define("state-history-chart-line", StateHistoryChartLine); diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index a1b7e30dc9..dd60599bc2 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -238,22 +238,24 @@ export class StatisticsChart extends LitElement { this.maxYAxis; if (typeof minYAxis === "number") { if (this.fitYData) { - minYAxis = ({ min }) => Math.min(min, this.minYAxis!); + minYAxis = ({ min }) => + Math.min(this._roundYAxis(min, Math.floor), this.minYAxis!); } } else if (this.logarithmicScale) { minYAxis = ({ min }) => { const value = min > 0 ? min * 0.95 : min * 1.05; - return Math.abs(value) < 1 ? value : Math.floor(value); + return this._roundYAxis(value, Math.floor); }; } if (typeof maxYAxis === "number") { if (this.fitYData) { - maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!); + maxYAxis = ({ max }) => + Math.max(this._roundYAxis(max, Math.ceil), this.maxYAxis!); } } else if (this.logarithmicScale) { maxYAxis = ({ max }) => { const value = max > 0 ? max * 1.05 : max * 0.95; - return Math.abs(value) < 1 ? value : Math.ceil(value); + return this._roundYAxis(value, Math.ceil); }; } const endTime = this.endTime ?? new Date(); @@ -634,6 +636,10 @@ export class StatisticsChart extends LitElement { return value; } + private _roundYAxis(value: number, roundingFn: (value: number) => number) { + return Math.abs(value) < 1 ? value : roundingFn(value); + } + static styles = css` :host { display: block; From 1349d9d8e335ebb28b09b38153ade54efbb35fa7 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 23 Jun 2025 03:40:16 -0400 Subject: [PATCH 081/216] Hide AI Task from default dashboard (#25877) --- src/panels/lovelace/common/generate-lovelace-config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 8e373cf384..16d348b731 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -35,6 +35,7 @@ import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; import type { EntityBadgeConfig } from "../badges/types"; const HIDE_DOMAIN = new Set([ + "ai_task", "automation", "configurator", "device_tracker", From 3e2a5bff4d6403694146c9b46c382fbef1041048 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 23 Jun 2025 11:07:19 +0300 Subject: [PATCH 082/216] Zwave delete device button on device page (#25766) * Handle ZWaveJS device delete in frontend according to status * finishing touches * Fix custom value selected when clicking item in combo box (#25734) * Assist Chat: handle intent progress delta not always being there (#25730) handle intent progress data type change * More support for no-grid energy dashboard (#25644) * More support for no-grid energy dashboard * Update src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts Co-authored-by: Petar Petrov * lint --------- Co-authored-by: Petar Petrov * Z-Wave: apply sentence-style capitalization (#25739) * Display full error for card preview mode (#25747) * Fix edit card not working in chrome after editing (#25751) * Change backup type order (#25759) * Fix alerts refresh on device page (#25748) * Fix alerts refresh on device page * don't reset actions periodically * reset stuff only on deviceId change * Ensure grid options always return an object (#25760) Assist Chat: handle intent progress delta not always being there (#25730) handle intent progress data type change More support for no-grid energy dashboard (#25644) * More support for no-grid energy dashboard * Update src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts Co-authored-by: Petar Petrov * lint --------- Co-authored-by: Petar Petrov Z-Wave: apply sentence-style capitalization (#25739) Display full error for card preview mode (#25747) Fix edit card not working in chrome after editing (#25751) Change backup type order (#25759) Fix alerts refresh on device page (#25748) * Fix alerts refresh on device page * don't reset actions periodically * reset stuff only on deviceId change Ensure grid options always return an object (#25760) * fix merge issue * lint * Update text and buttons * Update src/translations/en.json Co-authored-by: Norbert Rittel * update text --------- Co-authored-by: Paul Bottein Co-authored-by: Paulus Schoutsen Co-authored-by: karwosts <32912880+karwosts@users.noreply.github.com> Co-authored-by: c0ffeeca7 <38767475+c0ffeeca7@users.noreply.github.com> Co-authored-by: Norbert Rittel --- .../zwave_js/device-actions.ts | 62 ++++- .../dialog-zwave_js-remove-failed-node.ts | 234 ------------------ .../zwave_js/dialog-zwave_js-remove-node.ts | 184 +++++++++++--- ...show-dialog-zwave_js-remove-failed-node.ts | 19 -- .../show-dialog-zwave_js-remove-node.ts | 5 +- .../zwave_js/zwave_js-config-dashboard.ts | 8 +- .../zwave_js/zwave_js-provisioned.ts | 9 +- src/translations/en.json | 21 +- 8 files changed, 225 insertions(+), 317 deletions(-) delete mode 100644 src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-failed-node.ts delete mode 100644 src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node.ts diff --git a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts index ffe1a23305..96c36c045d 100644 --- a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts +++ b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts @@ -1,6 +1,7 @@ import { mdiChatQuestion, mdiCog, + mdiDelete, mdiDeleteForever, mdiHospitalBox, mdiInformation, @@ -16,17 +17,19 @@ import { fetchZwaveIsNodeFirmwareUpdateInProgress, fetchZwaveNetworkStatus, fetchZwaveNodeStatus, + fetchZwaveProvisioningEntries, + unprovisionZwaveSmartStartNode, } from "../../../../../../data/zwave_js"; import { showConfirmationDialog } from "../../../../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../../../../types"; import { showZWaveJSRebuildNodeRoutesDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-rebuild-node-routes"; import { showZWaveJSNodeStatisticsDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-node-statistics"; import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node"; -import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node"; import { showZWaveJSUpdateFirmwareNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-update-firmware-node"; import type { DeviceAction } from "../../../ha-config-device-page"; import { showZWaveJSHardResetControllerDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-hard-reset-controller"; import { showZWaveJSAddNodeDialog } from "../../../../integrations/integration-panels/zwave_js/add-node/show-dialog-zwave_js-add-node"; +import { showZWaveJSRemoveNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-node"; export const getZwaveDeviceActions = async ( el: HTMLElement, @@ -47,6 +50,43 @@ export const getZwaveDeviceActions = async ( const entryId = configEntry.entry_id; + const provisioningEntries = await fetchZwaveProvisioningEntries( + hass, + entryId + ); + const provisioningEntry = provisioningEntries.find( + (entry) => entry.device_id === device.id + ); + if (provisioningEntry && !provisioningEntry.nodeId) { + return [ + { + label: hass.localize("ui.panel.config.devices.delete_device"), + classes: "warning", + icon: mdiDelete, + action: async () => { + const confirm = await showConfirmationDialog(el, { + title: hass.localize( + "ui.panel.config.zwave_js.provisioned.confirm_unprovision_title" + ), + text: hass.localize( + "ui.panel.config.zwave_js.provisioned.confirm_unprovision_text", + { name: device.name_by_user || device.name } + ), + confirmText: hass.localize("ui.common.remove"), + destructive: true, + }); + + if (confirm) { + await unprovisionZwaveSmartStartNode( + hass, + entryId, + provisioningEntry.dsk + ); + } + }, + }, + ]; + } const nodeStatus = await fetchZwaveNodeStatus(hass, device.id); if (!nodeStatus) { @@ -84,16 +124,6 @@ export const getZwaveDeviceActions = async ( device, }), }, - { - label: hass.localize( - "ui.panel.config.zwave_js.device_info.remove_failed" - ), - icon: mdiDeleteForever, - action: () => - showZWaveJSRemoveFailedNodeDialog(el, { - device_id: device.id, - }), - }, { label: hass.localize( "ui.panel.config.zwave_js.device_info.node_statistics" @@ -103,6 +133,16 @@ export const getZwaveDeviceActions = async ( showZWaveJSNodeStatisticsDialog(el, { device, }), + }, + { + label: hass.localize("ui.panel.config.devices.delete_device"), + classes: "warning", + icon: mdiDelete, + action: () => + showZWaveJSRemoveNodeDialog(el, { + deviceId: device.id, + entryId, + }), } ); } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-failed-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-failed-node.ts deleted file mode 100644 index e5b46004a1..0000000000 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-failed-node.ts +++ /dev/null @@ -1,234 +0,0 @@ -import "@material/mwc-button/mwc-button"; -import { mdiCheckCircle, mdiCloseCircle, mdiRobotDead } from "@mdi/js"; -import type { UnsubscribeFunc } from "home-assistant-js-websocket"; -import type { CSSResultGroup } from "lit"; -import { css, html, LitElement, nothing } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { fireEvent } from "../../../../../common/dom/fire_event"; -import "../../../../../components/ha-spinner"; -import { createCloseHeading } from "../../../../../components/ha-dialog"; -import type { ZWaveJSRemovedNode } from "../../../../../data/zwave_js"; -import { removeFailedZwaveNode } from "../../../../../data/zwave_js"; -import { haStyleDialog } from "../../../../../resources/styles"; -import type { HomeAssistant } from "../../../../../types"; -import type { ZWaveJSRemoveFailedNodeDialogParams } from "./show-dialog-zwave_js-remove-failed-node"; - -@customElement("dialog-zwave_js-remove-failed-node") -class DialogZWaveJSRemoveFailedNode extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @state() private device_id?: string; - - @state() private _status = ""; - - @state() private _error?: any; - - @state() private _node?: ZWaveJSRemovedNode; - - private _subscribed?: Promise; - - public disconnectedCallback(): void { - super.disconnectedCallback(); - this._unsubscribe(); - } - - public async showDialog( - params: ZWaveJSRemoveFailedNodeDialogParams - ): Promise { - this.device_id = params.device_id; - } - - public closeDialog(): void { - this._unsubscribe(); - this.device_id = undefined; - this._status = ""; - - fireEvent(this, "dialog-closed", { dialog: this.localName }); - } - - public closeDialogFinished(): void { - history.back(); - this.closeDialog(); - } - - protected render() { - if (!this.device_id) { - return nothing; - } - - return html` - - ${this._status === "" - ? html` -
    - -
    - ${this.hass.localize( - "ui.panel.config.zwave_js.remove_failed_node.introduction" - )} -
    -
    - - ${this.hass.localize( - "ui.panel.config.zwave_js.remove_failed_node.remove_device" - )} - - ` - : ``} - ${this._status === "started" - ? html` -
    - -
    -

    - - ${this.hass.localize( - "ui.panel.config.zwave_js.remove_failed_node.in_progress" - )} - -

    -
    -
    - ` - : ``} - ${this._status === "failed" - ? html` -
    - -
    -

    - ${this.hass.localize( - "ui.panel.config.zwave_js.remove_failed_node.removal_failed" - )} -

    - ${this._error - ? html`

    ${this._error.message}

    ` - : ``} -
    -
    - - ${this.hass.localize("ui.common.close")} - - ` - : ``} - ${this._status === "finished" - ? html` -
    - -
    -

    - ${this.hass.localize( - "ui.panel.config.zwave_js.remove_failed_node.removal_finished", - { id: this._node!.node_id } - )} -

    -
    -
    - - ${this.hass.localize("ui.common.close")} - - ` - : ``} -
    - `; - } - - private _startExclusion(): void { - if (!this.hass) { - return; - } - this._status = "started"; - this._subscribed = removeFailedZwaveNode( - this.hass, - this.device_id!, - (message: any) => this._handleMessage(message) - ).catch((error) => { - this._status = "failed"; - this._error = error; - return undefined; - }); - } - - private _handleMessage(message: any): void { - if (message.event === "exclusion started") { - this._status = "started"; - } - if (message.event === "node removed") { - this._status = "finished"; - this._node = message.node; - this._unsubscribe(); - } - } - - private async _unsubscribe(): Promise { - if (this._subscribed) { - const unsubFunc = await this._subscribed; - if (unsubFunc instanceof Function) { - unsubFunc(); - } - this._subscribed = undefined; - } - if (this._status !== "finished") { - this._status = ""; - } - } - - static get styles(): CSSResultGroup { - return [ - haStyleDialog, - css` - .success { - color: var(--success-color); - } - - .failed { - color: var(--warning-color); - } - - .flex-container { - display: flex; - align-items: center; - } - - ha-svg-icon { - width: 68px; - height: 48px; - } - - .flex-container ha-spinner, - .flex-container ha-svg-icon { - margin-right: 20px; - margin-inline-end: 20px; - margin-inline-start: initial; - } - `, - ]; - } -} - -declare global { - interface HTMLElementTagNameMap { - "dialog-zwave_js-remove-failed-node": DialogZWaveJSRemoveFailedNode; - } -} diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-node.ts index 528e35740d..58e88b3c00 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-remove-node.ts @@ -2,6 +2,7 @@ import { mdiCheckCircle, mdiClose, mdiCloseCircle, + mdiRobotDead, mdiVectorSquareRemove, } from "@mdi/js"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; @@ -17,6 +18,14 @@ import "../../../../../components/ha-spinner"; import { haStyleDialog } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; import type { ZWaveJSRemoveNodeDialogParams } from "./show-dialog-zwave_js-remove-node"; +import { + fetchZwaveNodeStatus, + NodeStatus, + removeFailedZwaveNode, +} from "../../../../../data/zwave_js"; +import "../../../../../components/ha-list-item"; +import "../../../../../components/ha-icon-next"; +import type { DeviceRegistryEntry } from "../../../../../data/device_registry"; const EXCLUSION_TIMEOUT_SECONDS = 120; @@ -30,10 +39,16 @@ export interface ZWaveJSRemovedNode { class DialogZWaveJSRemoveNode extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @state() private entry_id?: string; + @state() private _entryId?: string; + + @state() private _deviceId?: string; + + private _device?: DeviceRegistryEntry; @state() private _step: | "start" + | "start_exclusion" + | "start_removal" | "exclusion" | "remove" | "finished" @@ -42,7 +57,7 @@ class DialogZWaveJSRemoveNode extends LitElement { @state() private _node?: ZWaveJSRemovedNode; - @state() private _removedCallback?: () => void; + @state() private _onClose?: () => void; private _removeNodeTimeoutHandle?: number; @@ -58,15 +73,23 @@ class DialogZWaveJSRemoveNode extends LitElement { public async showDialog( params: ZWaveJSRemoveNodeDialogParams ): Promise { - this.entry_id = params.entry_id; - this._removedCallback = params.removedCallback; - if (params.skipConfirmation) { + this._entryId = params.entryId; + this._deviceId = params.deviceId; + this._onClose = params.onClose; + if (this._deviceId) { + const nodeStatus = await fetchZwaveNodeStatus(this.hass, this._deviceId!); + this._device = this.hass.devices[this._deviceId]; + this._step = + nodeStatus.status === NodeStatus.Dead ? "start_removal" : "start"; + } else if (params.skipConfirmation) { this._startExclusion(); + } else { + this._step = "start_exclusion"; } } protected render() { - if (!this.entry_id) { + if (!this._entryId) { return nothing; } @@ -75,7 +98,12 @@ class DialogZWaveJSRemoveNode extends LitElement { ); return html` - + + + `; + } + + if (this._step === "start_removal") { + return html` + +

    + ${this.hass.localize( + "ui.panel.config.zwave_js.remove_node.failed_node_intro", + { name: this._device!.name_by_user || this._device!.name } + )} +

    + `; + } + + if (this._step === "start_exclusion") { + return html` + +

    + ${this.hass.localize( + "ui.panel.config.zwave_js.remove_node.exclusion_intro" + )} +

    `; } @@ -143,30 +212,59 @@ class DialogZWaveJSRemoveNode extends LitElement { `; } - private _renderAction(): TemplateResult { + private _renderAction() { + if (this._step === "start") { + return nothing; + } + + if (this._step === "start_removal") { + return html` + + ${this.hass.localize("ui.common.cancel")} + + + ${this.hass.localize("ui.common.remove")} + + `; + } + + if (this._step === "start_exclusion") { + return html` + + ${this.hass.localize("ui.common.cancel")} + + + ${this.hass.localize( + "ui.panel.config.zwave_js.remove_node.start_exclusion" + )} + + `; + } + return html` - + ${this.hass.localize( - this._step === "start" - ? "ui.panel.config.zwave_js.remove_node.start_exclusion" - : this._step === "exclusion" - ? "ui.panel.config.zwave_js.remove_node.cancel_exclusion" - : "ui.common.close" + this._step === "exclusion" + ? "ui.panel.config.zwave_js.remove_node.cancel_exclusion" + : "ui.common.close" )} `; } - private _startExclusion(): void { + private _startExclusion() { this._subscribed = this.hass.connection - .subscribeMessage((message) => this._handleMessage(message), { + .subscribeMessage(this._handleMessage, { type: "zwave_js/remove_node", - entry_id: this.entry_id, + entry_id: this._entryId, }) .catch((err) => { this._step = "failed"; @@ -180,7 +278,20 @@ class DialogZWaveJSRemoveNode extends LitElement { }, EXCLUSION_TIMEOUT_SECONDS * 1000); } - private _handleMessage(message: any): void { + private _startRemoval() { + this._subscribed = removeFailedZwaveNode( + this.hass, + this._deviceId!, + this._handleMessage + ).catch((err) => { + this._step = "failed"; + this._error = err.message; + return undefined; + }); + this._step = "remove"; + } + + private _handleMessage = (message: any) => { if (message.event === "exclusion failed") { this._unsubscribe(); this._step = "failed"; @@ -192,17 +303,14 @@ class DialogZWaveJSRemoveNode extends LitElement { this._step = "finished"; this._node = message.node; this._unsubscribe(); - if (this._removedCallback) { - this._removedCallback(); - } } - } + }; private _stopExclusion(): void { try { this.hass.callWS({ type: "zwave_js/stop_exclusion", - entry_id: this.entry_id, + entry_id: this._entryId, }); } catch (err) { // eslint-disable-next-line no-console @@ -224,10 +332,16 @@ class DialogZWaveJSRemoveNode extends LitElement { }; public closeDialog(): void { - this._unsubscribe(); - this.entry_id = undefined; - this._step = "start"; + this._entryId = undefined; + } + public handleDialogClosed(): void { + this._unsubscribe(); + this._entryId = undefined; + this._step = "start"; + if (this._onClose) { + this._onClose(); + } fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -266,6 +380,14 @@ class DialogZWaveJSRemoveNode extends LitElement { ha-alert { width: 100%; } + + .menu-options { + align-self: stretch; + } + + ha-list-item { + --mdc-list-side-padding: 24px; + } `, ]; } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node.ts deleted file mode 100644 index ae902f28ae..0000000000 --- a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { fireEvent } from "../../../../../common/dom/fire_event"; - -export interface ZWaveJSRemoveFailedNodeDialogParams { - device_id: string; -} - -export const loadRemoveFailedNodeDialog = () => - import("./dialog-zwave_js-remove-failed-node"); - -export const showZWaveJSRemoveFailedNodeDialog = ( - element: HTMLElement, - removeFailedNodeDialogParams: ZWaveJSRemoveFailedNodeDialogParams -): void => { - fireEvent(element, "show-dialog", { - dialogTag: "dialog-zwave_js-remove-failed-node", - dialogImport: loadRemoveFailedNodeDialog, - dialogParams: removeFailedNodeDialogParams, - }); -}; diff --git a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-node.ts index ac5fe5b063..e501a92140 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-node.ts @@ -1,9 +1,10 @@ import { fireEvent } from "../../../../../common/dom/fire_event"; export interface ZWaveJSRemoveNodeDialogParams { - entry_id: string; + entryId: string; + deviceId?: string; skipConfirmation?: boolean; - removedCallback?: () => void; + onClose?: () => void; } export const loadRemoveNodeDialog = () => diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts index f82c38de9c..e2505789ee 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts @@ -604,7 +604,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) { history.back(); } - private async _fetchData() { + private _fetchData = async () => { if (!this.configEntryId) { return; } @@ -638,7 +638,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) { this._dataCollectionOptIn = dataCollectionStatus.opted_in === true || dataCollectionStatus.enabled === true; - } + }; private async _addNodeClicked() { this._openInclusionDialog(); @@ -646,10 +646,10 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) { private async _removeNodeClicked() { showZWaveJSRemoveNodeDialog(this, { - entry_id: this.configEntryId!, + entryId: this.configEntryId!, skipConfirmation: this._network?.controller.inclusion_state === InclusionState.Excluding, - removedCallback: () => this._fetchData(), + onClose: this._fetchData, }); } diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts index 38a26852bd..e4b13608d6 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-provisioned.ts @@ -123,18 +123,21 @@ class ZWaveJSProvisioned extends LitElement { } private _unprovision = async (ev) => { - const dsk = ev.currentTarget.provisioningEntry.dsk; + const { dsk, nodeId } = ev.currentTarget.provisioningEntry; const confirm = await showConfirmationDialog(this, { title: this.hass.localize( "ui.panel.config.zwave_js.provisioned.confirm_unprovision_title" ), text: this.hass.localize( - "ui.panel.config.zwave_js.provisioned.confirm_unprovision_text" + nodeId + ? "ui.panel.config.zwave_js.provisioned.confirm_unprovision_text_included" + : "ui.panel.config.zwave_js.provisioned.confirm_unprovision_text" ), confirmText: this.hass.localize( - "ui.panel.config.zwave_js.provisioned.unprovison" + "ui.panel.config.zwave_js.provisioned.unprovision" ), + destructive: true, }); if (!confirm) { diff --git a/src/translations/en.json b/src/translations/en.json index 964324323c..18cdf3d5c4 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5880,7 +5880,6 @@ "installer_settings": "Installer settings", "reinterview_device": "Re-interview", "rebuild_routes": "Rebuild routes", - "remove_failed": "Remove failed", "update_firmware": "Update", "highest_security": "Highest security", "hard_reset_controller": "Factory reset", @@ -6126,8 +6125,8 @@ "unprovision": "Unprovision", "included": "Included", "not_included": "Not Included", - "confirm_unprovision_title": "Are you sure you want to unprovision the device?", - "confirm_unprovision_text": "If you unprovision the device it will not be added to Home Assistant when it is powered on. If it is already added to Home Assistant, removing the provisioned device will not remove it from Home Assistant." + "confirm_unprovision_title": "Remove device?", + "confirm_unprovision_text": "{name} will be permanently removed from Home Assistant and your Z-Wave network." }, "security_classes": { "None": { @@ -6152,22 +6151,18 @@ }, "remove_node": { "title": "Remove a Z-Wave device", - "introduction": "Remove a device from your Z-Wave network, and remove the associated device and entities from Home Assistant.", + "introduction": "There are two ways to remove a device from your Z-Wave network depending on the state of the device. If the device is working, you can remove it by following the instructions that came with your device to put it into exclusion mode. If the device is unavailable, you can force-remove it from the network.", + "exclusion_intro": "Remove a device from your Z-Wave network, and remove the associated device and entities from Home Assistant.", + "failed_node_intro": "{name} is unable to connect to your Z-wave Network. Home Assistant can force-remove a device for you. After removing it you might need to factory reset your device.", + "menu_exclude_device": "Remove a working device", + "menu_remove_device": "Force-remove an unavailable device", "start_exclusion": "Start exclusion", "cancel_exclusion": "Cancel exclusion", "follow_device_instructions": "Follow the directions that came with your device to trigger exclusion on the device.", "removing_device": "Removing device", - "exclusion_failed": "An error occurred during exclusion. Please check the logs for more information.", + "exclusion_failed": "An error occurred. Please check the logs for more information.", "exclusion_finished": "Device {id} has been removed from your Z-Wave network." }, - "remove_failed_node": { - "title": "Remove a failed Z-Wave device", - "introduction": "Remove a failed device from your Z-Wave network. Use this if you are unable to exclude a device normally because it is broken.", - "remove_device": "Remove device", - "in_progress": "The device removal is in progress.", - "removal_finished": "Device {id} has been removed from your Z-Wave network.", - "removal_failed": "The device could not be removed from your Z-Wave network." - }, "reinterview_node": { "title": "Re-interview a Z-Wave device", "introduction": "Re-interview a device on your Z-Wave network. Use this feature if your device has missing or incorrect functionality.", From 82da36825e9a14c0778895e84173213c4b95071d Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 23 Jun 2025 12:25:46 +0300 Subject: [PATCH 083/216] Fix duplicated requests in statistics-graph (#25878) --- .../cards/hui-statistics-graph-card.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index d7be2a06d2..7d493f1575 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -97,8 +97,8 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } if (this._config?.energy_date_selection) { this._subscribeEnergy(); - } else { - this._setFetchStatisticsTimer(); + } else if (this._interval === undefined) { + this._setFetchStatisticsTimer(true); } } @@ -213,9 +213,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { changedProps.has("_config") && oldConfig?.entities !== this._config.entities ) { - this._getStatisticsMetaData(this._entities).then(() => { - this._setFetchStatisticsTimer(); - }); + this._setFetchStatisticsTimer(true); return; } @@ -230,10 +228,14 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } } - private _setFetchStatisticsTimer() { - this._getStatistics(); - // statistics are created every hour + private async _setFetchStatisticsTimer(fetchMetadata = false) { clearInterval(this._interval); + this._interval = 0; // block concurrent calls + if (fetchMetadata) { + await this._getStatisticsMetaData(this._entities); + } + await this._getStatistics(); + // statistics are created every hour if (!this._config?.energy_date_selection) { this._interval = window.setInterval( () => this._getStatistics(), From c73122d7ee2141da5f5e230408a5aa8b48de49cb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 23 Jun 2025 14:10:27 +0200 Subject: [PATCH 084/216] Don't send double card updated event when rendering the card (#25883) --- src/panels/lovelace/cards/hui-card.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index 271e2cd027..12be541b86 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -224,7 +224,9 @@ export class HuiCard extends ReactiveElement { this._element.preview = this.preview; // For backwards compatibility (this._element as any).editMode = this.preview; - fireEvent(this, "card-updated"); + if (this.hasUpdated) { + fireEvent(this, "card-updated"); + } } catch (e: any) { // eslint-disable-next-line no-console console.error(this.config?.type, e); From a5005c0840c1771019e7b93f7fa5cac124cc5fc4 Mon Sep 17 00:00:00 2001 From: ildar170975 <71872483+ildar170975@users.noreply.github.com> Date: Tue, 24 Jun 2025 07:25:09 +0300 Subject: [PATCH 085/216] Device page: sort related automations, scenes, scripts (#24742) * sort related items * use memoize for sorted _related * fix for "_entities" & "_getEntitiesSorted" --- .../config/devices/ha-config-device-page.ts | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 44530ab333..4220645aaa 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -15,6 +15,7 @@ import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; +import type { HassEntity } from "home-assistant-js-websocket"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { ASSIST_ENTITIES, SENSOR_ENTITIES } from "../../../common/const"; import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; @@ -185,6 +186,27 @@ export class HaConfigDevicePage extends LitElement { ) ); + private _getEntitiesSorted = (entities: HassEntity[]) => + entities.sort((ent1, ent2) => + stringCompare( + ent1.attributes.friendly_name || `zzz${ent1.entity_id}`, + ent2.attributes.friendly_name || `zzz${ent2.entity_id}`, + this.hass.locale.language + ) + ); + + private _getRelated = memoizeOne((related?: RelatedResult) => ({ + automation: this._getEntitiesSorted( + (related?.automation ?? []).map((entityId) => this.hass.states[entityId]) + ), + scene: this._getEntitiesSorted( + (related?.scene ?? []).map((entityId) => this.hass.states[entityId]) + ), + script: this._getEntitiesSorted( + (related?.script ?? []).map((entityId) => this.hass.states[entityId]) + ), + })); + private _deviceIdInList = memoizeOne((deviceId: string) => [deviceId]); private _entityIds = memoizeOne( @@ -433,23 +455,25 @@ export class HaConfigDevicePage extends LitElement { ${this._related?.automation?.length ? html`
    - ${this._related.automation.map((automation) => { - const entityState = this.hass.states[automation]; - return entityState - ? html` - - ${computeStateName(entityState)} - - - ` - : nothing; - })} + ${this._getRelated(this._related).automation.map( + (automation) => { + const entityState = automation; + return entityState + ? html` + + ${computeStateName(entityState)} + + + ` + : nothing; + } + )}
    ` : html` @@ -510,8 +534,8 @@ export class HaConfigDevicePage extends LitElement { ${this._related?.scene?.length ? html`
    - ${this._related.scene.map((scene) => { - const entityState = this.hass.states[scene]; + ${this._getRelated(this._related).scene.map((scene) => { + const entityState = scene; return entityState && entityState.attributes.id ? html` - ${this._related.script.map((script) => { - const entityState = this.hass.states[script]; + ${this._getRelated(this._related).script.map((script) => { + const entityState = script; const entry = this._entityReg.find( - (e) => e.entity_id === script + (e) => e.entity_id === script.entity_id ); let url = `/config/script/show/${entityState.entity_id}`; if (entry) { From b3f5eb256f4212013cd2203894d727294490562e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 06:25:30 +0200 Subject: [PATCH 086/216] Update dependency eslint-plugin-import to v2.32.0 (#25890) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index a92ec0f7bc..d9266af1d0 100644 --- a/package.json +++ b/package.json @@ -188,7 +188,7 @@ "eslint-config-airbnb-base": "15.0.0", "eslint-config-prettier": "10.1.5", "eslint-import-resolver-webpack": "0.13.10", - "eslint-plugin-import": "2.31.0", + "eslint-plugin-import": "2.32.0", "eslint-plugin-lit": "2.1.1", "eslint-plugin-lit-a11y": "5.0.1", "eslint-plugin-unused-imports": "4.1.4", diff --git a/yarn.lock b/yarn.lock index fb26be3a89..9744bd65f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5807,7 +5807,7 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.8": +"array-includes@npm:^3.1.9": version: 3.1.9 resolution: "array-includes@npm:3.1.9" dependencies: @@ -5830,7 +5830,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.5": +"array.prototype.findlastindex@npm:^1.2.6": version: 1.2.6 resolution: "array.prototype.findlastindex@npm:1.2.6" dependencies: @@ -5845,7 +5845,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.flat@npm:^1.3.2": +"array.prototype.flat@npm:^1.3.3": version: 1.3.3 resolution: "array.prototype.flat@npm:1.3.3" dependencies: @@ -5857,7 +5857,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.flatmap@npm:^1.3.2": +"array.prototype.flatmap@npm:^1.3.3": version: 1.3.3 resolution: "array.prototype.flatmap@npm:1.3.3" dependencies: @@ -7937,7 +7937,7 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.12.0": +"eslint-module-utils@npm:^2.12.1": version: 2.12.1 resolution: "eslint-module-utils@npm:2.12.1" dependencies: @@ -7949,32 +7949,32 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-import@npm:2.31.0": - version: 2.31.0 - resolution: "eslint-plugin-import@npm:2.31.0" +"eslint-plugin-import@npm:2.32.0": + version: 2.32.0 + resolution: "eslint-plugin-import@npm:2.32.0" dependencies: "@rtsao/scc": "npm:^1.1.0" - array-includes: "npm:^3.1.8" - array.prototype.findlastindex: "npm:^1.2.5" - array.prototype.flat: "npm:^1.3.2" - array.prototype.flatmap: "npm:^1.3.2" + array-includes: "npm:^3.1.9" + array.prototype.findlastindex: "npm:^1.2.6" + array.prototype.flat: "npm:^1.3.3" + array.prototype.flatmap: "npm:^1.3.3" debug: "npm:^3.2.7" doctrine: "npm:^2.1.0" eslint-import-resolver-node: "npm:^0.3.9" - eslint-module-utils: "npm:^2.12.0" + eslint-module-utils: "npm:^2.12.1" hasown: "npm:^2.0.2" - is-core-module: "npm:^2.15.1" + is-core-module: "npm:^2.16.1" is-glob: "npm:^4.0.3" minimatch: "npm:^3.1.2" object.fromentries: "npm:^2.0.8" object.groupby: "npm:^1.0.3" - object.values: "npm:^1.2.0" + object.values: "npm:^1.2.1" semver: "npm:^6.3.1" - string.prototype.trimend: "npm:^1.0.8" + string.prototype.trimend: "npm:^1.0.9" tsconfig-paths: "npm:^3.15.0" peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 - checksum: 10/6b76bd009ac2db0615d9019699d18e2a51a86cb8c1d0855a35fb1b418be23b40239e6debdc6e8c92c59f1468ed0ea8d7b85c817117a113d5cc225be8a02ad31c + checksum: 10/1bacf4967e9ebf99e12176a795f0d6d3a87d1c9a030c2207f27b267e10d96a1220be2647504c7fc13ab543cdf13ffef4b8f5620e0447032dba4ff0d3922f7c9e languageName: node linkType: hard @@ -9366,7 +9366,7 @@ __metadata: eslint-config-airbnb-base: "npm:15.0.0" eslint-config-prettier: "npm:10.1.5" eslint-import-resolver-webpack: "npm:0.13.10" - eslint-plugin-import: "npm:2.31.0" + eslint-plugin-import: "npm:2.32.0" eslint-plugin-lit: "npm:2.1.1" eslint-plugin-lit-a11y: "npm:5.0.1" eslint-plugin-unused-imports: "npm:4.1.4" @@ -9908,7 +9908,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.0": +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.0, is-core-module@npm:^2.16.1": version: 2.16.1 resolution: "is-core-module@npm:2.16.1" dependencies: @@ -11702,7 +11702,7 @@ __metadata: languageName: node linkType: hard -"object.values@npm:^1.2.0": +"object.values@npm:^1.2.1": version: 1.2.1 resolution: "object.values@npm:1.2.1" dependencies: @@ -13752,7 +13752,7 @@ __metadata: languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.8, string.prototype.trimend@npm:^1.0.9": +"string.prototype.trimend@npm:^1.0.9": version: 1.0.9 resolution: "string.prototype.trimend@npm:1.0.9" dependencies: From dd64fa228c3c68b09a3500bf261eb9fdada88428 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 24 Jun 2025 07:23:58 +0200 Subject: [PATCH 087/216] Add color options to area card (#25881) * Add color options to area card * Color all controls with the same color * Clean area card --- .../card-features/hui-area-controls-card-feature.ts | 9 ++------- src/panels/lovelace/cards/hui-area-card.ts | 12 +++++++++++- src/panels/lovelace/cards/types.ts | 1 + .../editor/config-elements/hui-area-card-editor.ts | 9 ++------- src/translations/en.json | 1 + 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts index ff6a8366a7..2c75e2303c 100644 --- a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -2,7 +2,6 @@ import { mdiFan, mdiLightbulb, mdiToggleSwitch } from "@mdi/js"; import { callService, type HassEntity } from "home-assistant-js-websocket"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { generateEntityFilter, @@ -25,7 +24,6 @@ import { AREA_CONTROLS } from "./types"; interface AreaControlsButton { iconPath: string; - activeColor: string; onService: string; offService: string; filter: EntityFilter; @@ -37,7 +35,6 @@ export const AREA_CONTROLS_BUTTONS: Record = { filter: { domain: "light", }, - activeColor: "var(--state-light-active-color)", onService: "light.turn_on", offService: "light.turn_off", }, @@ -46,7 +43,6 @@ export const AREA_CONTROLS_BUTTONS: Record = { filter: { domain: "fan", }, - activeColor: "var(--state-fan-active-color)", onService: "fan.turn_on", offService: "fan.turn_off", }, @@ -55,7 +51,6 @@ export const AREA_CONTROLS_BUTTONS: Record = { filter: { domain: "switch", }, - activeColor: "var(--state-switch-active-color)", onService: "switch.turn_on", offService: "switch.turn_off", }, @@ -223,7 +218,6 @@ class HuiAreaControlsCardFeature return html` @@ -240,7 +234,8 @@ class HuiAreaControlsCardFeature cardFeatureStyles, css` ha-control-button { - --active-color: var(--primary-color); + --active-color: var(--state-active-color); + --control-button-focus-color: var(--state-active-color); } ha-control-button.active { --control-button-background-color: var(--active-color); diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index 5c8eb2888e..f91dc6bec0 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -10,7 +10,9 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; +import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; +import { computeCssColor } from "../../../common/color/compute-color"; import { BINARY_STATE_ON } from "../../../common/const"; import { computeAreaName } from "../../../common/entity/compute_area_name"; import { generateEntityFilter } from "../../../common/entity/entity_filter"; @@ -428,8 +430,16 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { const ignoreAspectRatio = this.layout === "grid" || this.layout === "panel"; + const color = this._config.color + ? computeCssColor(this._config.color) + : undefined; + + const style = { + "--tile-color": color, + }; + return html` - +
    Date: Tue, 24 Jun 2025 08:27:16 +0300 Subject: [PATCH 088/216] hui-graph-footer-editor: add margin to ha-switch to prevent a scrollbar (#25645) * add padding-bottom to card-config to prevent a scrollbar * revert padding for card-config * add margin to ha-switch * revert margin for ha-switch * set margin for ha-switch --- .../editor/config-elements/hui-graph-footer-editor.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts b/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts index 706f18177d..e3f49edc23 100644 --- a/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-graph-footer-editor.ts @@ -1,5 +1,5 @@ import type { CSSResultGroup } from "lit"; -import { html, LitElement, nothing } from "lit"; +import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { assert } from "superstruct"; import type { HASSDomEvent } from "../../../../common/dom/fire_event"; @@ -139,7 +139,14 @@ export class HuiGraphFooterEditor } static get styles(): CSSResultGroup { - return configElementStyle; + return [ + configElementStyle, + css` + .card-config ha-switch { + margin: 1px 0; + } + `, + ]; } } From 77c458a0e5289c73ac7fc8bfcac8cae20540c38d Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 17 Jun 2025 16:07:57 +0300 Subject: [PATCH 089/216] Reduce reset-zoom button size on timeline charts (#25796) --- src/components/chart/ha-chart-base.ts | 14 +++++++++++++- .../chart/state-history-chart-timeline.ts | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 37a12f07e6..b40256b60b 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -49,6 +49,9 @@ export class HaChartBase extends LitElement { @property({ attribute: "expand-legend", type: Boolean }) public expandLegend?: boolean; + @property({ attribute: "small-controls", type: Boolean }) + public smallControls?: boolean; + // extraComponents is not reactive and should not trigger updates public extraComponents?: any[]; @@ -194,7 +197,7 @@ export class HaChartBase extends LitElement {
    ${this._renderLegend()} -
    +
    ${this._isZoomed ? html` `; From 6738b7d708c68014a06a452f83e1e617876afd17 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 17 Jun 2025 16:14:33 +0300 Subject: [PATCH 090/216] Fix sankey total calculation to account for `included_in_stat` (#25805) --- src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts index da162673e3..5f1a9cf146 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sankey-card.ts @@ -238,7 +238,6 @@ class HuiEnergySankeyCard if (value < 0.01) { return; } - untrackedConsumption -= value; const node = { id: device.stat_consumption, label: @@ -260,6 +259,8 @@ class HuiEnergySankeyCard source: node.parent, target: node.id, }); + } else { + untrackedConsumption -= value; } deviceNodes.push(node); }); From d764187e8c8bb0abbb06aead2d3953d693014ae4 Mon Sep 17 00:00:00 2001 From: Anthony Relle Date: Tue, 17 Jun 2025 10:54:34 +0100 Subject: [PATCH 091/216] Update ElectricityMaps URL in Energy Dashboard (#25816) fix: update electricitymap domain --- .../lovelace/cards/energy/hui-energy-distribution-card.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts index a84858d13e..1d7d7d4a14 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-distribution-card.ts @@ -204,7 +204,7 @@ class HuiEnergyDistrubutionCard let homeHighCarbonCircumference: number | undefined; // This fallback is used in the demo - let electricityMapUrl = "https://app.electricitymap.org"; + let electricityMapUrl = "https://app.electricitymaps.com"; if (this._data.co2SignalEntity && this._data.fossilEnergyConsumption) { // Calculate high carbon consumption From 51246f119c5c11ed33d823b0045160272e316889 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 17 Jun 2025 15:14:07 +0200 Subject: [PATCH 092/216] Fix disabled color in dark mode in production (#25818) --- src/common/style/derived-css-vars.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common/style/derived-css-vars.ts b/src/common/style/derived-css-vars.ts index 65e6f21d06..3b2add7e1b 100644 --- a/src/common/style/derived-css-vars.ts +++ b/src/common/style/derived-css-vars.ts @@ -9,7 +9,9 @@ const _extractCssVars = ( cssString.split(";").forEach((rawLine) => { const line = rawLine.substring(rawLine.indexOf("--")).trim(); if (line.startsWith("--") && condition(line)) { - const [name, value] = line.split(":").map((part) => part.trim()); + const [name, value] = line + .split(":") + .map((part) => part.replaceAll("}", "").trim()); variables[name.substring(2, name.length)] = value; } }); @@ -25,7 +27,10 @@ export const extractVar = (css: CSSResult, varName: string) => { } const endIndex = cssString.indexOf(";", startIndex + search.length); - return cssString.substring(startIndex + search.length, endIndex).trim(); + return cssString + .substring(startIndex + search.length, endIndex) + .replaceAll("}", "") + .trim(); }; export const extractVars = (css: CSSResult) => { From 7f7e6935475272ad1b7e29f082a56487fba2276c Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Thu, 19 Jun 2025 18:49:58 +0300 Subject: [PATCH 093/216] Fix bar chart data order when using the legend (#25832) * Fix bar chart data order when using the legend * type fix --- src/components/chart/ha-chart-base.ts | 77 ++++++++++--------- .../hui-energy-devices-detail-graph-card.ts | 11 ++- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index b40256b60b..fdaad85c83 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -9,6 +9,7 @@ import type { LegendComponentOption, XAXisOption, YAXisOption, + LineSeriesOption, } from "echarts/types/dist/shared"; import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; @@ -642,44 +643,46 @@ export class HaChartBase extends LitElement { const yAxis = (this.options?.yAxis?.[0] ?? this.options?.yAxis) as | YAXisOption | undefined; - const series = ensureArray(this.data) - .filter((d) => !this._hiddenDatasets.has(String(d.name ?? d.id))) - .map((s) => { - if (s.type === "line") { - if (yAxis?.type === "log") { - // set <=0 values to null so they render as gaps on a log graph - return { - ...s, - data: s.data?.map((v) => - Array.isArray(v) - ? [ - v[0], - typeof v[1] !== "number" || v[1] > 0 ? v[1] : null, - ...v.slice(2), - ] - : v - ), - }; - } - if (s.sampling === "minmax") { - const minX = - xAxis?.min && typeof xAxis.min === "number" - ? xAxis.min - : undefined; - const maxX = - xAxis?.max && typeof xAxis.max === "number" - ? xAxis.max - : undefined; - return { - ...s, - sampling: undefined, - data: downSampleLineData(s.data, this.clientWidth, minX, maxX), - }; - } + const series = ensureArray(this.data).map((s) => { + const data = this._hiddenDatasets.has(String(s.name ?? s.id)) + ? undefined + : s.data; + if (data && s.type === "line") { + if (yAxis?.type === "log") { + // set <=0 values to null so they render as gaps on a log graph + return { + ...s, + data: (data as LineSeriesOption["data"])!.map((v) => + Array.isArray(v) + ? [ + v[0], + typeof v[1] !== "number" || v[1] > 0 ? v[1] : null, + ...v.slice(2), + ] + : v + ), + }; } - return s; - }); - return series; + if (s.sampling === "minmax") { + const minX = + xAxis?.min && typeof xAxis.min === "number" ? xAxis.min : undefined; + const maxX = + xAxis?.max && typeof xAxis.max === "number" ? xAxis.max : undefined; + return { + ...s, + sampling: undefined, + data: downSampleLineData( + data as LineSeriesOption["data"], + this.clientWidth, + minX, + maxX + ), + }; + } + } + return { ...s, data }; + }); + return series as ECOption["series"]; } private _getDefaultHeight() { diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts index 917d9e5950..c4ef01c62a 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts @@ -81,7 +81,6 @@ export class HuiEnergyDevicesDetailGraphCard key: this._config?.collection_key, }).subscribe((data) => { this._data = data; - this._processStatistics(); }), ]; } @@ -103,10 +102,7 @@ export class HuiEnergyDevicesDetailGraphCard } protected willUpdate(changedProps: PropertyValues) { - if ( - (changedProps.has("_hiddenStats") || changedProps.has("_config")) && - this._data - ) { + if (changedProps.has("_config") || changedProps.has("_data")) { this._processStatistics(); } } @@ -206,7 +202,10 @@ export class HuiEnergyDevicesDetailGraphCard ); private _processStatistics() { - const energyData = this._data!; + if (!this._data) { + return; + } + const energyData = this._data; this._start = energyData.start; this._end = energyData.end || endOfToday(); From 5ac6781f7dc7b14e8ca46a9a092e26e0a59b59ea Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 18 Jun 2025 17:44:27 +0200 Subject: [PATCH 094/216] Remove debug type in secondary line in statistic picker (#25835) --- src/components/entity/ha-statistic-picker.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/entity/ha-statistic-picker.ts b/src/components/entity/ha-statistic-picker.ts index 3d20ab7a43..24f96b0eb2 100644 --- a/src/components/entity/ha-statistic-picker.ts +++ b/src/components/entity/ha-statistic-picker.ts @@ -438,10 +438,8 @@ export class HaStatisticPicker extends LitElement { ` : nothing} ${item.primary} - ${item.secondary || item.type - ? html`${item.secondary} - ${item.type}` + ${item.secondary + ? html`${item.secondary}` : nothing} ${item.statistic_id && showEntityId ? html` From e6fbe0d5387975268a3094dbff300104b9565bd8 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 23 Jun 2025 08:42:54 +0300 Subject: [PATCH 095/216] Round chart limits with fit_y_data (#25851) --- src/components/chart/state-history-chart-line.ts | 14 ++++++++++---- src/components/chart/statistics-chart.ts | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index f8f7689539..e87ae2ac0e 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -226,22 +226,24 @@ export class StateHistoryChartLine extends LitElement { this.maxYAxis; if (typeof minYAxis === "number") { if (this.fitYData) { - minYAxis = ({ min }) => Math.min(min, this.minYAxis!); + minYAxis = ({ min }) => + Math.min(this._roundYAxis(min, Math.floor), this.minYAxis!); } } else if (this.logarithmicScale) { minYAxis = ({ min }) => { const value = min > 0 ? min * 0.95 : min * 1.05; - return Math.abs(value) < 1 ? value : Math.floor(value); + return this._roundYAxis(value, Math.floor); }; } if (typeof maxYAxis === "number") { if (this.fitYData) { - maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!); + maxYAxis = ({ max }) => + Math.max(this._roundYAxis(max, Math.ceil), this.maxYAxis!); } } else if (this.logarithmicScale) { maxYAxis = ({ max }) => { const value = max > 0 ? max * 1.05 : max * 0.95; - return Math.abs(value) < 1 ? value : Math.ceil(value); + return this._roundYAxis(value, Math.ceil); }; } this._chartOptions = { @@ -767,6 +769,10 @@ export class StateHistoryChartLine extends LitElement { } return value; } + + private _roundYAxis(value: number, roundingFn: (value: number) => number) { + return Math.abs(value) < 1 ? value : roundingFn(value); + } } customElements.define("state-history-chart-line", StateHistoryChartLine); diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index a1b7e30dc9..dd60599bc2 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -238,22 +238,24 @@ export class StatisticsChart extends LitElement { this.maxYAxis; if (typeof minYAxis === "number") { if (this.fitYData) { - minYAxis = ({ min }) => Math.min(min, this.minYAxis!); + minYAxis = ({ min }) => + Math.min(this._roundYAxis(min, Math.floor), this.minYAxis!); } } else if (this.logarithmicScale) { minYAxis = ({ min }) => { const value = min > 0 ? min * 0.95 : min * 1.05; - return Math.abs(value) < 1 ? value : Math.floor(value); + return this._roundYAxis(value, Math.floor); }; } if (typeof maxYAxis === "number") { if (this.fitYData) { - maxYAxis = ({ max }) => Math.max(max, this.maxYAxis!); + maxYAxis = ({ max }) => + Math.max(this._roundYAxis(max, Math.ceil), this.maxYAxis!); } } else if (this.logarithmicScale) { maxYAxis = ({ max }) => { const value = max > 0 ? max * 1.05 : max * 0.95; - return Math.abs(value) < 1 ? value : Math.ceil(value); + return this._roundYAxis(value, Math.ceil); }; } const endTime = this.endTime ?? new Date(); @@ -634,6 +636,10 @@ export class StatisticsChart extends LitElement { return value; } + private _roundYAxis(value: number, roundingFn: (value: number) => number) { + return Math.abs(value) < 1 ? value : roundingFn(value); + } + static styles = css` :host { display: block; From 1a8ff83e2d75ba1b8aeb1d82c317156b97d44c70 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Sun, 22 Jun 2025 13:11:29 +0300 Subject: [PATCH 096/216] Another fix for history chart axis rounding (#25852) --- .../chart/state-history-chart-line.ts | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index e87ae2ac0e..362d7ab579 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -731,20 +731,17 @@ export class StateHistoryChartLine extends LitElement { } private _formatYAxisLabel = (value: number) => { - const formatOptions = - value >= 1 || value <= -1 - ? undefined - : { - // show the first significant digit for tiny values - maximumFractionDigits: Math.max( - 2, - // use the difference to the previous value to determine the number of significant digits #25526 - -Math.floor( - Math.log10(Math.abs(value - this._previousYAxisLabelValue || 1)) - ) - ), - }; - const label = formatNumber(value, this.hass.locale, formatOptions); + // show the first significant digit for tiny values + const maximumFractionDigits = Math.max( + 1, + // use the difference to the previous value to determine the number of significant digits #25526 + -Math.floor( + Math.log10(Math.abs(value - this._previousYAxisLabelValue || 1)) + ) + ); + const label = formatNumber(value, this.hass.locale, { + maximumFractionDigits, + }); const width = measureTextWidth(label, 12) + 5; if (width > this._yWidth) { this._yWidth = width; From 73cd1e8e9d59bd074e45d9a9c6bd90dee7ba5d42 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 23 Jun 2025 12:25:46 +0300 Subject: [PATCH 097/216] Fix duplicated requests in statistics-graph (#25878) --- .../cards/hui-statistics-graph-card.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index d7be2a06d2..7d493f1575 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -97,8 +97,8 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } if (this._config?.energy_date_selection) { this._subscribeEnergy(); - } else { - this._setFetchStatisticsTimer(); + } else if (this._interval === undefined) { + this._setFetchStatisticsTimer(true); } } @@ -213,9 +213,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { changedProps.has("_config") && oldConfig?.entities !== this._config.entities ) { - this._getStatisticsMetaData(this._entities).then(() => { - this._setFetchStatisticsTimer(); - }); + this._setFetchStatisticsTimer(true); return; } @@ -230,10 +228,14 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { } } - private _setFetchStatisticsTimer() { - this._getStatistics(); - // statistics are created every hour + private async _setFetchStatisticsTimer(fetchMetadata = false) { clearInterval(this._interval); + this._interval = 0; // block concurrent calls + if (fetchMetadata) { + await this._getStatisticsMetaData(this._entities); + } + await this._getStatistics(); + // statistics are created every hour if (!this._config?.energy_date_selection) { this._interval = window.setInterval( () => this._getStatistics(), From 45e66d8b6e3c6530e6aaeffb6886fe64b7808dec Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 23 Jun 2025 14:10:27 +0200 Subject: [PATCH 098/216] Don't send double card updated event when rendering the card (#25883) --- src/panels/lovelace/cards/hui-card.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index 271e2cd027..12be541b86 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -224,7 +224,9 @@ export class HuiCard extends ReactiveElement { this._element.preview = this.preview; // For backwards compatibility (this._element as any).editMode = this.preview; - fireEvent(this, "card-updated"); + if (this.hasUpdated) { + fireEvent(this, "card-updated"); + } } catch (e: any) { // eslint-disable-next-line no-console console.error(this.config?.type, e); From 47b90feffa149cf18ee8f9772a9b1c6f8beb8baa Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 24 Jun 2025 09:58:11 +0200 Subject: [PATCH 099/216] Bumped version to 20250531.4 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 59884f9957..17bcc756c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250531.3" +version = "20250531.4" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From 641a2eb77cd5c109f3c7c1ffd1a0e46f9121caee Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 24 Jun 2025 13:46:12 +0200 Subject: [PATCH 100/216] Use area card in area strategy (#25879) * Bring area card to area strategy * Add device classes * Use subview --- src/components/ha-floor-icon.ts | 16 +++ .../strategies/areas/area-view-strategy.ts | 6 - .../areas/areas-dashboard-strategy.ts | 1 + .../areas/areas-overview-view-strategy.ts | 133 ++++++++++-------- 4 files changed, 92 insertions(+), 64 deletions(-) diff --git a/src/components/ha-floor-icon.ts b/src/components/ha-floor-icon.ts index 595f8add1a..a36b7ec391 100644 --- a/src/components/ha-floor-icon.ts +++ b/src/components/ha-floor-icon.ts @@ -30,6 +30,22 @@ export const floorDefaultIconPath = ( return mdiHome; }; +export const floorDefaultIcon = (floor: Pick) => { + switch (floor.level) { + case 0: + return "mdi:home-floor-0"; + case 1: + return "mdi:home-floor-1"; + case 2: + return "mdi:home-floor-2"; + case 3: + return "mdi:home-floor-3"; + case -1: + return "mdi:home-floor-negative-1"; + } + return "mdi:home"; +}; + @customElement("ha-floor-icon") export class HaFloorIcon extends LitElement { @property({ attribute: false }) public floor!: Pick< diff --git a/src/panels/lovelace/strategies/areas/area-view-strategy.ts b/src/panels/lovelace/strategies/areas/area-view-strategy.ts index 2fc8a0256d..3ce785fbfd 100644 --- a/src/panels/lovelace/strategies/areas/area-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/area-view-strategy.ts @@ -191,12 +191,6 @@ export class AreaViewStrategy extends ReactiveElement { type: "sections", header: { badges_position: "bottom", - layout: "responsive", - card: { - type: "markdown", - text_only: true, - content: `## ${area.name}`, - }, }, max_columns: maxColumns, sections: sections, diff --git a/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts b/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts index ef397b4a8e..3b882837bf 100644 --- a/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts @@ -66,6 +66,7 @@ export class AreasDashboardStrategy extends ReactiveElement { return { title: area.name, path: path, + subview: true, strategy: { type: "area", area: area.area_id, diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index c8cda13888..74bbd0a38c 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -1,15 +1,15 @@ import { ReactiveElement } from "lit"; import { customElement } from "lit/decorators"; +import { stringCompare } from "../../../../common/string/compare"; +import { floorDefaultIcon } from "../../../../components/ha-floor-icon"; import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; import type { HomeAssistant } from "../../../../types"; +import { getAreaControlEntities } from "../../card-features/hui-area-controls-card-feature"; +import type { AreaControl } from "../../card-features/types"; +import type { AreaCardConfig, HeadingCardConfig } from "../../cards/types"; import type { EntitiesDisplay } from "./area-view-strategy"; -import { - computeAreaPath, - computeAreaTileCardConfig, - getAreaGroupedEntities, - getAreas, -} from "./helpers/areas-strategy-helper"; +import { computeAreaPath, getAreas } from "./helpers/areas-strategy-helper"; interface AreaOptions { groups_options?: Record; @@ -36,71 +36,88 @@ export class AreasOverviewViewStrategy extends ReactiveElement { config.areas_display?.order ); - const areaSections = areas - .map((area) => { - const path = computeAreaPath(area.area_id); + const floors = Object.values(hass.floors); + floors.sort((floorA, floorB) => { + if (floorA.level !== floorB.level) { + return (floorA.level ?? 0) - (floorB.level ?? 0); + } + return stringCompare(floorA.name, floorB.name); + }); - const areaConfig = config.areas_options?.[area.area_id]; - - const groups = getAreaGroupedEntities( - area.area_id, - hass, - areaConfig?.groups_options + const floorSections = [ + ...floors, + { floor_id: "default", name: "Default", level: null, icon: null }, + ] + .map((floor) => { + const areasInFloors = areas.filter( + (area) => + area.floor_id === floor.floor_id || + (!area.floor_id && floor.floor_id === "default") ); - const entities = [ - ...groups.lights, - ...groups.covers, - ...groups.climate, - ...groups.media_players, - ...groups.security, - ...groups.actions, - ...groups.others, - ]; + if (areasInFloors.length === 0) { + return undefined; + } - const computeTileCard = computeAreaTileCardConfig(hass, area.name); + const areasCards = areasInFloors.map((area) => { + const path = computeAreaPath(area.area_id); + + const controls: AreaControl[] = ["light", "fan"]; + const controlEntities = getAreaControlEntities( + controls, + area.area_id, + hass + ); + + const filteredControls = controls.filter( + (control) => controlEntities[control].length > 0 + ); + + return { + type: "area", + area: area.area_id, + display_type: "compact", + sensor_classes: ["temperature", "humidity"], + alert_classes: [ + "water_leak", + "smoke", + "gas", + "co", + "motion", + "occupancy", + "presence", + ], + features: filteredControls.length + ? [ + { + type: "area-controls", + controls: filteredControls, + }, + ] + : [], + navigation_path: path, + }; + }); + + const headingCard: HeadingCardConfig = { + type: "heading", + heading_style: "title", + heading: floor.name, + icon: floor.icon || floorDefaultIcon(floor), + }; return { + max_columns: 3, type: "grid", - cards: [ - { - type: "heading", - heading: area.name, - icon: area.icon || undefined, - badges: [ - ...(area.temperature_entity_id - ? [{ entity: area.temperature_entity_id }] - : []), - ...(area.humidity_entity_id - ? [{ entity: area.humidity_entity_id }] - : []), - ], - tap_action: { - action: "navigate", - navigation_path: path, - }, - }, - ...(entities.length - ? entities.map(computeTileCard) - : [ - { - type: "markdown", - content: hass.localize( - "ui.panel.lovelace.strategy.areas.no_entities" - ), - }, - ]), - ], + cards: [headingCard, ...areasCards], }; }) - .filter( - (section): section is LovelaceSectionConfig => section !== undefined - ); + ?.filter((section) => section !== undefined); return { type: "sections", max_columns: 3, - sections: areaSections, + sections: floorSections || [], }; } } From fb28c8971bd62f8a4417df2e38450ea30935fcba Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Tue, 24 Jun 2025 15:31:26 +0300 Subject: [PATCH 101/216] Update Z-Wave text (#25893) --- .../zwave_js/add-node/zwave-js-add-node-configure-device.ts | 4 +--- .../integration-panels/zwave_js/zwave_js-config-dashboard.ts | 2 +- src/translations/en.json | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/zwave_js/add-node/zwave-js-add-node-configure-device.ts b/src/panels/config/integrations/integration-panels/zwave_js/add-node/zwave-js-add-node-configure-device.ts index 721c1a9417..9155086490 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/add-node/zwave-js-add-node-configure-device.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/add-node/zwave-js-add-node-configure-device.ts @@ -80,9 +80,7 @@ export class ZWaveJsAddNodeConfigureDevice extends LitElement { options: [ { value: Protocols.ZWaveLongRange.toString(), - label: localize( - "ui.panel.config.zwave_js.add_node.configure_device.long_range_label" - ), + label: "Long Range", // brand name and we should not translate that description: localize( "ui.panel.config.zwave_js.add_node.configure_device.long_range_description" ), diff --git a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts index e2505789ee..70776bb5bd 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/zwave_js-config-dashboard.ts @@ -414,7 +414,7 @@ class ZWaveJSConfigDashboard extends SubscribeMixin(LitElement) { InclusionState.SmartStart)} > ${this.hass.localize( - "ui.panel.config.zwave_js.common.remove_node" + "ui.panel.config.zwave_js.common.remove_a_node" )} Date: Tue, 24 Jun 2025 16:20:30 +0300 Subject: [PATCH 102/216] Update buttons in Z-Wave firmware update dialog (#25894) Co-authored-by: Bram Kragten --- .../dialog-zwave_js-update-firmware-node.ts | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts index 706d394b85..cf068f7ec5 100644 --- a/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts +++ b/src/panels/config/integrations/integration-panels/zwave_js/dialog-zwave_js-update-firmware-node.ts @@ -1,4 +1,3 @@ -import "@material/mwc-button/mwc-button"; import "@material/mwc-linear-progress/mwc-linear-progress"; import { mdiCheckCircle, mdiCloseCircle, mdiFileUpload } from "@mdi/js"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; @@ -37,6 +36,7 @@ import { } from "../../../../../dialogs/generic/show-dialog-box"; import { haStyleDialog } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; +import "../../../../../components/ha-button"; import type { ZWaveJSUpdateFirmwareNodeDialogParams } from "./show-dialog-zwave_js-update-firmware-node"; const firmwareTargetSchema: HaFormSchema[] = [ @@ -130,7 +130,7 @@ class DialogZWaveJSUpdateFirmwareNode extends LitElement { .schema=${firmwareTargetSchema} @value-changed=${this._firmwareTargetChanged} >`} - `; + `; const status = this._updateFinishedMessage ? this._updateFinishedMessage.success @@ -153,13 +153,23 @@ class DialogZWaveJSUpdateFirmwareNode extends LitElement { const abortFirmwareUpdateButton = this._nodeStatus.is_controller_node ? nothing : html` - + ${this.hass.localize( "ui.panel.config.zwave_js.update_firmware.abort" )} - + `; + const closeButton = html` + + ${this.hass.localize("ui.common.close")} + + `; + return html` - ${abortFirmwareUpdateButton} + ${abortFirmwareUpdateButton} ${closeButton} ` : this._updateProgressMessage && !this._updateFinishedMessage ? html` @@ -242,7 +252,7 @@ class DialogZWaveJSUpdateFirmwareNode extends LitElement { } )}

    - ${abortFirmwareUpdateButton} + ${abortFirmwareUpdateButton} ${closeButton} ` : html`
    From bfcc36bb40e9ca443959f7f4273eeeb9a45cde85 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 24 Jun 2025 15:27:07 +0200 Subject: [PATCH 103/216] Update integration page layout (#25880) --- .../config/entities/ha-config-entities.ts | 4 +- .../integrations/dialog-pick-config-entry.ts | 98 + .../ha-config-entry-device-row.ts | 223 +++ .../integrations/ha-config-entry-row.ts | 761 ++++++++ .../ha-config-integration-page.ts | 1710 +++++------------ .../integrations/ha-config-sub-entry-row.ts | 250 +++ .../show-pick-config-entry-dialog.ts | 20 + src/resources/theme/color.globals.ts | 5 + src/translations/en.json | 7 + 9 files changed, 1857 insertions(+), 1221 deletions(-) create mode 100644 src/panels/config/integrations/dialog-pick-config-entry.ts create mode 100644 src/panels/config/integrations/ha-config-entry-device-row.ts create mode 100644 src/panels/config/integrations/ha-config-entry-row.ts create mode 100644 src/panels/config/integrations/ha-config-sub-entry-row.ts create mode 100644 src/panels/config/integrations/show-pick-config-entry-dialog.ts diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 538b9af027..e1607485cb 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -1115,9 +1115,10 @@ ${ const domain = this._searchParms.get("domain"); const configEntry = this._searchParms.get("config_entry"); const subEntry = this._searchParms.get("sub_entry"); + const device = this._searchParms.get("device"); const label = this._searchParms.has("label"); - if (!domain && !configEntry && !label) { + if (!domain && !configEntry && !label && !device) { return; } @@ -1126,6 +1127,7 @@ ${ this._filters = { "ha-filter-states": [], "ha-filter-integrations": domain ? [domain] : [], + "ha-filter-devices": device ? [device] : [], config_entry: configEntry ? [configEntry] : [], sub_entry: subEntry ? [subEntry] : [], }; diff --git a/src/panels/config/integrations/dialog-pick-config-entry.ts b/src/panels/config/integrations/dialog-pick-config-entry.ts new file mode 100644 index 0000000000..218e180f42 --- /dev/null +++ b/src/panels/config/integrations/dialog-pick-config-entry.ts @@ -0,0 +1,98 @@ +import { mdiClose } from "@mdi/js"; +import { LitElement, css, html, nothing } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import { fireEvent } from "../../../common/dom/fire_event"; +import "../../../components/ha-dialog-header"; +import "../../../components/ha-icon-button"; +import "../../../components/ha-md-dialog"; +import type { HaMdDialog } from "../../../components/ha-md-dialog"; +import "../../../components/ha-md-list"; +import "../../../components/ha-md-list-item"; +import { ERROR_STATES, RECOVERABLE_STATES } from "../../../data/config_entries"; +import type { HomeAssistant } from "../../../types"; +import type { PickConfigEntryDialogParams } from "./show-pick-config-entry-dialog"; + +@customElement("dialog-pick-config-entry") +export class DialogPickConfigEntry extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _params?: PickConfigEntryDialogParams; + + @query("ha-md-dialog") private _dialog?: HaMdDialog; + + public showDialog(params: PickConfigEntryDialogParams): void { + this._params = params; + } + + private _dialogClosed(): void { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + public closeDialog() { + this._dialog?.close(); + return true; + } + + protected render() { + if (!this._params) { + return nothing; + } + return html` + + + + ${this.hass.localize( + `component.${this._params.domain}.config_subentries.${this._params.subFlowType}.initiate_flow.user` + )} + + + ${this._params.configEntries.map( + (entry) => + html`${entry.title}` + )} + + + `; + } + + private _itemPicked(ev: Event) { + this._params?.configEntryPicked((ev.currentTarget as any).entry); + this.closeDialog(); + } + + static styles = css` + :host { + --dialog-content-padding: 0; + } + @media all and (min-width: 600px) { + ha-dialog { + --mdc-dialog-min-width: 400px; + } + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-pick-config-entry": DialogPickConfigEntry; + } +} diff --git a/src/panels/config/integrations/ha-config-entry-device-row.ts b/src/panels/config/integrations/ha-config-entry-device-row.ts new file mode 100644 index 0000000000..d122f3c67b --- /dev/null +++ b/src/panels/config/integrations/ha-config-entry-device-row.ts @@ -0,0 +1,223 @@ +import { + mdiCogOutline, + mdiDelete, + mdiDotsVertical, + mdiStopCircleOutline, +} from "@mdi/js"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; +import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; +import { getDeviceContext } from "../../../common/entity/context/get_device_context"; +import { navigate } from "../../../common/navigate"; +import type { ConfigEntry } from "../../../data/config_entries"; +import { + removeConfigEntryFromDevice, + updateDeviceRegistryEntry, + type DeviceRegistryEntry, +} from "../../../data/device_registry"; +import type { EntityRegistryEntry } from "../../../data/entity_registry"; +import { haStyle } from "../../../resources/styles"; +import type { HomeAssistant } from "../../../types"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../lovelace/custom-card-helpers"; +import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail"; +import "./ha-config-sub-entry-row"; + +@customElement("ha-config-entry-device-row") +class HaConfigEntryDeviceRow extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean, reflect: true }) public narrow = false; + + @property({ attribute: false }) public entry!: ConfigEntry; + + @property({ attribute: false }) public device!: DeviceRegistryEntry; + + @property({ attribute: false }) public entities!: EntityRegistryEntry[]; + + protected render() { + const device = this.device; + + const entities = this._getEntities(); + + const { area } = getDeviceContext(device, this.hass); + + const supportingText = [ + device.model || device.sw_version || device.manufacturer, + area ? area.name : undefined, + ].filter(Boolean); + + return html` +
    ${computeDeviceNameDisplay(device, this.hass)}
    + ${supportingText.join(" • ")} + ${supportingText.length && entities.length ? " • " : nothing} + ${ + entities.length + ? html`
    ${this.hass.localize( + "ui.panel.config.integrations.config_entry.entities", + { count: entities.length } + )}` + : nothing + } + + + +
    + ${ + !this.narrow + ? html`` + : nothing + } + + + + ${ + this.narrow + ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.device.configure" + )} + ` + : nothing + } + + + + ${ + device.disabled_by && device.disabled_by !== "user" + ? this.hass.localize( + "ui.dialogs.device-registry-detail.enabled_cause", + { + type: this.hass.localize( + `ui.dialogs.device-registry-detail.type.${ + device.entry_type || "device" + }` + ), + cause: this.hass.localize( + `config_entry.disabled_by.${device.disabled_by}` + ), + } + ) + : device.disabled_by + ? this.hass.localize( + "ui.panel.config.integrations.config_entry.device.enable" + ) + : this.hass.localize( + "ui.panel.config.integrations.config_entry.device.disable" + ) + } + + + + ${ + this.entry.supports_remove_device + ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.device.delete" + )} + ` + : nothing + } + + `; + } + + private _getEntities = (): EntityRegistryEntry[] => + this.entities?.filter((entity) => entity.device_id === this.device.id); + + private _handleConfigureDevice() { + showDeviceRegistryDetailDialog(this, { + device: this.device, + updateEntry: async (updates) => { + await updateDeviceRegistryEntry(this.hass, this.device.id, updates); + }, + }); + } + + private async _handleDisableDevice() { + await updateDeviceRegistryEntry(this.hass, this.device.id, { + disabled_by: this.device.disabled_by === "user" ? null : "user", + }); + } + + private async _handleDeleteDevice() { + const entry = this.entry; + const confirmed = await showConfirmationDialog(this, { + text: this.hass.localize("ui.panel.config.devices.confirm_delete"), + confirmText: this.hass.localize("ui.common.delete"), + dismissText: this.hass.localize("ui.common.cancel"), + destructive: true, + }); + + if (!confirmed) { + return; + } + + try { + await removeConfigEntryFromDevice( + this.hass!, + this.device.id, + entry.entry_id + ); + } catch (err: any) { + showAlertDialog(this, { + title: this.hass.localize("ui.panel.config.devices.error_delete"), + text: err.message, + }); + } + } + + private _handleNavigateToDevice() { + navigate(`/config/devices/device/${this.device.id}`); + } + + static styles = [ + haStyle, + css` + :host { + border-top: 1px solid var(--divider-color); + } + ha-md-list-item { + --md-list-item-leading-space: 56px; + } + .vertical-divider { + height: 100%; + width: 1px; + background: var(--divider-color); + } + a { + text-decoration: none; + } + `, + ]; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-entry-device-row": HaConfigEntryDeviceRow; + } +} diff --git a/src/panels/config/integrations/ha-config-entry-row.ts b/src/panels/config/integrations/ha-config-entry-row.ts new file mode 100644 index 0000000000..2d1c80a3fe --- /dev/null +++ b/src/panels/config/integrations/ha-config-entry-row.ts @@ -0,0 +1,761 @@ +import { + mdiAlertCircle, + mdiChevronDown, + mdiChevronUp, + mdiCogOutline, + mdiDelete, + mdiDevices, + mdiDotsVertical, + mdiDownload, + mdiHandExtendedOutline, + mdiPlayCircleOutline, + mdiPlus, + mdiProgressHelper, + mdiReload, + mdiReloadAlert, + mdiRenameBox, + mdiShapeOutline, + mdiStopCircleOutline, + mdiWrench, +} from "@mdi/js"; +import type { PropertyValues, TemplateResult } from "lit"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import memoizeOne from "memoize-one"; +import { isDevVersion } from "../../../common/config/version"; +import { + deleteApplicationCredential, + fetchApplicationCredentialsConfigEntry, +} from "../../../data/application_credential"; +import { getSignedPath } from "../../../data/auth"; +import type { + ConfigEntry, + DisableConfigEntryResult, + SubEntry, +} from "../../../data/config_entries"; +import { + deleteConfigEntry, + disableConfigEntry, + enableConfigEntry, + ERROR_STATES, + getSubEntries, + RECOVERABLE_STATES, + reloadConfigEntry, + updateConfigEntry, +} from "../../../data/config_entries"; +import type { DeviceRegistryEntry } from "../../../data/device_registry"; +import type { DiagnosticInfo } from "../../../data/diagnostics"; +import { getConfigEntryDiagnosticsDownloadUrl } from "../../../data/diagnostics"; +import type { EntityRegistryEntry } from "../../../data/entity_registry"; +import type { IntegrationManifest } from "../../../data/integration"; +import { + domainToName, + fetchIntegrationManifest, + integrationsWithPanel, +} from "../../../data/integration"; +import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entry-system-options/show-dialog-config-entry-system-options"; +import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; +import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow"; +import { showSubConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-sub-config-flow"; +import type { HomeAssistant } from "../../../types"; +import { documentationUrl } from "../../../util/documentation-url"; +import { fileDownload } from "../../../util/file_download"; +import { + showAlertDialog, + showConfirmationDialog, + showPromptDialog, +} from "../../lovelace/custom-card-helpers"; +import "./ha-config-entry-device-row"; +import { renderConfigEntryError } from "./ha-config-integration-page"; +import "./ha-config-sub-entry-row"; +import { haStyle } from "../../../resources/styles"; + +@customElement("ha-config-entry-row") +class HaConfigEntryRow extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean, reflect: true }) public narrow = false; + + @property({ attribute: false }) public manifest?: IntegrationManifest; + + @property({ attribute: false }) public diagnosticHandler?: DiagnosticInfo; + + @property({ attribute: false }) public entities!: EntityRegistryEntry[]; + + @property({ attribute: false }) public entry!: ConfigEntry; + + @state() private _expanded = true; + + @state() private _devicesExpanded = true; + + @state() private _subEntries?: SubEntry[]; + + protected willUpdate(changedProperties: PropertyValues): void { + if (changedProperties.has("entry")) { + this._fetchSubEntries(); + } + } + + protected render() { + const item = this.entry; + + let stateText: Parameters | undefined; + let stateTextExtra: TemplateResult | string | undefined; + let icon: string = mdiAlertCircle; + + if (!item.disabled_by && item.state === "not_loaded") { + stateText = ["ui.panel.config.integrations.config_entry.not_loaded"]; + } else if (item.state === "setup_in_progress") { + icon = mdiProgressHelper; + stateText = [ + "ui.panel.config.integrations.config_entry.setup_in_progress", + ]; + } else if (ERROR_STATES.includes(item.state)) { + if (item.state === "setup_retry") { + icon = mdiReloadAlert; + } + stateText = [ + `ui.panel.config.integrations.config_entry.state.${item.state}`, + ]; + stateTextExtra = renderConfigEntryError(this.hass, item); + } + + const devices = this._getDevices(); + const services = this._getServices(); + const entities = this._getEntities(); + + const ownDevices = [...devices, ...services].filter( + (device) => + !device.config_entries_subentries[item.entry_id].length || + device.config_entries_subentries[item.entry_id][0] === null + ); + + const statusLine: (TemplateResult | string)[] = []; + + if (item.disabled_by) { + statusLine.push( + this.hass.localize( + "ui.panel.config.integrations.config_entry.disable.disabled_cause", + { + cause: + this.hass.localize( + `ui.panel.config.integrations.config_entry.disable.disabled_by.${item.disabled_by}` + ) || item.disabled_by, + } + ) + ); + if (item.state === "failed_unload") { + statusLine.push(`. + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_restart_confirm" + )}.`); + } + } else if (!devices.length && !services.length && entities.length) { + statusLine.push( + html`${entities.length} entities` + ); + } + + const configPanel = this._configPanel(item.domain, this.hass.panels); + + const subEntries = this._subEntries || []; + + return html` + 0, + })} + > + ${subEntries.length || ownDevices.length + ? html`` + : nothing} +
    + ${item.title || domainToName(this.hass.localize, item.domain)} +
    +
    +
    ${statusLine}
    + ${stateText + ? html` +
    + +
    + ${this.hass.localize(...stateText)}${stateTextExtra + ? html`: ${stateTextExtra}` + : nothing} +
    +
    + ` + : nothing} +
    + ${item.disabled_by === "user" + ? html` + ${this.hass.localize("ui.common.enable")} + ` + : configPanel && + (item.domain !== "matter" || + isDevVersion(this.hass.config.version)) && + !stateText + ? html` + ` + : item.supports_options + ? html` + + + ` + : nothing} + + + ${devices.length + ? html` + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.devices`, + { count: devices.length } + )} + + + ` + : nothing} + ${services.length + ? html` + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.services`, + { count: services.length } + )} + + ` + : nothing} + ${entities.length + ? html` + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.entities`, + { count: entities.length } + )} + + + ` + : nothing} + ${!item.disabled_by && + RECOVERABLE_STATES.includes(item.state) && + item.supports_unload && + item.source !== "system" + ? html` + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.reload" + )} + + ` + : nothing} + + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.rename" + )} + + + ${Object.keys(item.supported_subentry_types).map( + (flowType) => + html` + + ${this.hass.localize( + `component.${item.domain}.config_subentries.${flowType}.initiate_flow.user` + )}` + )} + + + + ${this.diagnosticHandler && item.state === "loaded" + ? html` + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.download_diagnostics" + )} + + ` + : nothing} + ${!item.disabled_by && + item.supports_reconfigure && + item.source !== "system" + ? html` + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.reconfigure" + )} + + ` + : nothing} + + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.system_options" + )} + + ${item.disabled_by === "user" + ? html` + + + ${this.hass.localize("ui.common.enable")} + + ` + : item.source !== "system" + ? html` + + + ${this.hass.localize("ui.common.disable")} + + ` + : nothing} + ${item.source !== "system" + ? html` + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.delete" + )} + + ` + : nothing} + +
    + ${this._expanded + ? subEntries.length + ? html` + + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.devices_without_subentry" + )} + + ${this._devicesExpanded + ? ownDevices.map( + (device) => + html`` + ) + : nothing} + + ${subEntries.map( + (subEntry) => html` + + ` + )}` + : html` + ${ownDevices.map( + (device) => + html`` + )} + ` + : nothing} +
    `; + } + + private async _fetchSubEntries() { + this._subEntries = this.entry.num_subentries + ? await getSubEntries(this.hass, this.entry.entry_id) + : undefined; + } + + private _configPanel = memoizeOne( + (domain: string, panels: HomeAssistant["panels"]): string | undefined => + Object.values(panels).find( + (panel) => panel.config_panel_domain === domain + )?.url_path || integrationsWithPanel[domain] + ); + + private _getEntities = (): EntityRegistryEntry[] => + this.entities.filter( + (entity) => entity.config_entry_id === this.entry.entry_id + ); + + private _getDevices = (): DeviceRegistryEntry[] => + Object.values(this.hass.devices).filter( + (device) => + device.config_entries.includes(this.entry.entry_id) && + device.entry_type !== "service" + ); + + private _getServices = (): DeviceRegistryEntry[] => + Object.values(this.hass.devices).filter( + (device) => + device.config_entries.includes(this.entry.entry_id) && + device.entry_type === "service" + ); + + private _toggleExpand() { + this._expanded = !this._expanded; + } + + private _toggleOwnDevices() { + this._devicesExpanded = !this._devicesExpanded; + } + + private _showOptions() { + showOptionsFlowDialog(this, this.entry, { manifest: this.manifest }); + } + + // Return an application credentials id for this config entry to prompt the + // user for removal. This is best effort so we don't stop overall removal + // if the integration isn't loaded or there is some other error. + private async _applicationCredentialForRemove(entryId: string) { + try { + return (await fetchApplicationCredentialsConfigEntry(this.hass, entryId)) + .application_credentials_id; + } catch (_err: any) { + // We won't prompt the user to remove credentials + return null; + } + } + + private async _removeApplicationCredential(applicationCredentialsId: string) { + const confirmed = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_title" + ), + text: html`${this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_prompt" + )}, +
    +
    + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_detail" + )} +
    +
    + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.learn_more" + )} + `, + destructive: true, + confirmText: this.hass.localize("ui.common.remove"), + dismissText: this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.dismiss" + ), + }); + if (!confirmed) { + return; + } + try { + await deleteApplicationCredential(this.hass, applicationCredentialsId); + } catch (err: any) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_error_title" + ), + text: err.message, + }); + } + } + + private async _handleReload() { + const result = await reloadConfigEntry(this.hass, this.entry.entry_id); + const locale_key = result.require_restart + ? "reload_restart_confirm" + : "reload_confirm"; + showAlertDialog(this, { + text: this.hass.localize( + `ui.panel.config.integrations.config_entry.${locale_key}` + ), + }); + } + + private async _handleReconfigure() { + showConfigFlowDialog(this, { + startFlowHandler: this.entry.domain, + showAdvanced: this.hass.userData?.showAdvanced, + manifest: await fetchIntegrationManifest(this.hass, this.entry.domain), + entryId: this.entry.entry_id, + navigateToResult: true, + }); + } + + private async _handleRename() { + const newName = await showPromptDialog(this, { + title: this.hass.localize("ui.panel.config.integrations.rename_dialog"), + defaultValue: this.entry.title, + inputLabel: this.hass.localize( + "ui.panel.config.integrations.rename_input_label" + ), + }); + if (newName === null) { + return; + } + await updateConfigEntry(this.hass, this.entry.entry_id, { + title: newName, + }); + } + + private async _signUrl(ev) { + const anchor = ev.currentTarget; + ev.preventDefault(); + const signedUrl = await getSignedPath( + this.hass, + anchor.getAttribute("href") + ); + fileDownload(signedUrl.path); + } + + private async _handleDisable() { + const entryId = this.entry.entry_id; + + const confirmed = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_confirm_title", + { title: this.entry.title } + ), + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_confirm_text" + ), + confirmText: this.hass!.localize("ui.common.disable"), + dismissText: this.hass!.localize("ui.common.cancel"), + destructive: true, + }); + + if (!confirmed) { + return; + } + let result: DisableConfigEntryResult; + try { + result = await disableConfigEntry(this.hass, entryId); + } catch (err: any) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_error" + ), + text: err.message, + }); + return; + } + if (result.require_restart) { + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_restart_confirm" + ), + }); + } + } + + private async _handleEnable() { + const entryId = this.entry.entry_id; + + let result: DisableConfigEntryResult; + try { + result = await enableConfigEntry(this.hass, entryId); + } catch (err: any) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_error" + ), + text: err.message, + }); + return; + } + + if (result.require_restart) { + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.enable_restart_confirm" + ), + }); + } + } + + private async _handleDelete() { + const entryId = this.entry.entry_id; + + const applicationCredentialsId = + await this._applicationCredentialForRemove(entryId); + + const confirmed = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.delete_confirm_title", + { title: this.entry.title } + ), + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.delete_confirm_text" + ), + confirmText: this.hass!.localize("ui.common.delete"), + dismissText: this.hass!.localize("ui.common.cancel"), + destructive: true, + }); + + if (!confirmed) { + return; + } + const result = await deleteConfigEntry(this.hass, entryId); + + if (result.require_restart) { + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.restart_confirm" + ), + }); + } + if (applicationCredentialsId) { + this._removeApplicationCredential(applicationCredentialsId); + } + } + + private _handleSystemOptions() { + showConfigEntrySystemOptionsDialog(this, { + entry: this.entry, + manifest: this.manifest, + }); + } + + private _addSubEntry(ev) { + showSubConfigFlowDialog(this, this.entry, ev.target.flowType, { + startFlowHandler: this.entry.entry_id, + }); + } + + static styles = [ + haStyle, + css` + .expand-button { + margin: 0 -12px; + } + ha-md-list { + border: 1px solid var(--divider-color); + border-radius: var(--ha-card-border-radius, 12px); + padding: 0; + } + ha-md-list.devices { + margin: 16px; + margin-top: 0; + } + a ha-icon-button { + color: var( + --md-list-item-trailing-icon-color, + var(--md-sys-color-on-surface-variant, #49454f) + ); + } + `, + ]; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-entry-row": HaConfigEntryRow; + } +} diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index a60d0b32b9..148739aaaa 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -1,77 +1,37 @@ import { - mdiAlertCircle, - mdiBookshelf, mdiBug, mdiBugPlay, mdiBugStop, - mdiCog, - mdiDelete, - mdiDevices, mdiDotsVertical, - mdiDownload, mdiFileCodeOutline, - mdiHandExtendedOutline, + mdiHelpCircleOutline, mdiOpenInNew, mdiPackageVariant, - mdiPlayCircleOutline, mdiPlus, - mdiProgressHelper, - mdiReload, - mdiReloadAlert, - mdiRenameBox, - mdiShapeOutline, - mdiStopCircleOutline, mdiWeb, - mdiWrench, } from "@mdi/js"; import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { classMap } from "lit/directives/class-map"; import { until } from "lit/directives/until"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; -import { isDevVersion } from "../../../common/config/version"; import { caseInsensitiveStringCompare } from "../../../common/string/compare"; import { nextRender } from "../../../common/util/render-status"; import "../../../components/ha-button"; -import "../../../components/ha-card"; -import "../../../components/ha-md-divider"; -import "../../../components/ha-list-item"; import "../../../components/ha-md-button-menu"; +import "../../../components/ha-md-divider"; import "../../../components/ha-md-list"; import "../../../components/ha-md-list-item"; import "../../../components/ha-md-menu-item"; -import { - deleteApplicationCredential, - fetchApplicationCredentialsConfigEntry, -} from "../../../data/application_credential"; import { getSignedPath } from "../../../data/auth"; -import type { - ConfigEntry, - DisableConfigEntryResult, - SubEntry, -} from "../../../data/config_entries"; -import { - ERROR_STATES, - RECOVERABLE_STATES, - deleteConfigEntry, - deleteSubEntry, - disableConfigEntry, - enableConfigEntry, - getConfigEntries, - getSubEntries, - reloadConfigEntry, - updateConfigEntry, -} from "../../../data/config_entries"; +import type { ConfigEntry } from "../../../data/config_entries"; +import { ERROR_STATES, getConfigEntries } from "../../../data/config_entries"; import { ATTENTION_SOURCES } from "../../../data/config_flow"; import type { DeviceRegistryEntry } from "../../../data/device_registry"; import type { DiagnosticInfo } from "../../../data/diagnostics"; -import { - fetchDiagnosticHandler, - getConfigEntryDiagnosticsDownloadUrl, -} from "../../../data/diagnostics"; +import { fetchDiagnosticHandler } from "../../../data/diagnostics"; import type { EntityRegistryEntry } from "../../../data/entity_registry"; import { subscribeEntityRegistry } from "../../../data/entity_registry"; import { fetchEntitySourcesWithCache } from "../../../data/entity_sources"; @@ -85,18 +45,13 @@ import { domainToName, fetchIntegrationManifest, integrationIssuesUrl, - integrationsWithPanel, setIntegrationLogLevel, subscribeLogInfo, } from "../../../data/integration"; -import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entry-system-options/show-dialog-config-entry-system-options"; +import { QUALITY_SCALE_MAP } from "../../../data/integration_quality_scale"; import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; -import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow"; -import { - showAlertDialog, - showConfirmationDialog, - showPromptDialog, -} from "../../../dialogs/generic/show-dialog-box"; +import { showSubConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-sub-config-flow"; +import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-error-screen"; import "../../../layouts/hass-subpage"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; @@ -105,10 +60,10 @@ import type { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; import { documentationUrl } from "../../../util/documentation-url"; import { fileDownload } from "../../../util/file_download"; +import "./ha-config-entry-row"; import type { DataEntryFlowProgressExtended } from "./ha-config-integrations"; import { showAddIntegrationDialog } from "./show-add-integration-dialog"; -import { QUALITY_SCALE_MAP } from "../../../data/integration_quality_scale"; -import { showSubConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-sub-config-flow"; +import { showPickConfigEntryDialog } from "./show-pick-config-entry-dialog"; export const renderConfigEntryError = ( hass: HomeAssistant, @@ -175,15 +130,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { @state() private _domainEntities: Record = {}; - @state() private _subEntries: Record = {}; - - private _configPanel = memoizeOne( - (domain: string, panels: HomeAssistant["panels"]): string | undefined => - Object.values(panels).find( - (panel) => panel.config_panel_domain === domain - )?.url_path || integrationsWithPanel[domain] - ); - private _domainConfigEntries = memoizeOne( (domain: string, configEntries?: ConfigEntry[]): ConfigEntry[] => configEntries @@ -225,12 +171,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { this._fetchDiagnostics(); this._fetchEntitySources(); } - if ( - changedProperties.has("configEntries") || - changedProperties.has("_extraConfigEntries") - ) { - this._fetchSubEntries(); - } } private async _fetchEntitySources() { @@ -270,6 +210,14 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { this._extraConfigEntries || this.configEntries ); + const supportedSubentryTypes = new Set(); + + configEntries.forEach((entry) => { + Object.keys(entry.supported_subentry_types).forEach((type) => { + supportedSubentryTypes.add(type); + }); + }); + const configEntriesInProgress = this._domainConfigEntriesInProgress( this.domain, this.configEntriesInProgress @@ -309,7 +257,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ); }); - const devices = this._getDevices(configEntries, this.hass.devices); + const devicesRegs = this._getDevices(configEntries, this.hass.devices); const entities = this._getEntities(configEntries, this._entities); let numberOfEntities = entities.length; @@ -328,242 +276,278 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { } } - const services = !devices.some((device) => device.entry_type !== "service"); + const services = devicesRegs.filter( + (device) => device.entry_type === "service" + ); + const devices = devicesRegs.filter( + (device) => device.entry_type !== "service" + ); return html` - -
    -
    - -
    -
    - ${domainToName(this.hass.localize, -
    - ${this._manifest?.version != null - ? html`
    ${this._manifest.version}
    ` - : nothing} - ${this._manifest?.is_built_in === false - ? html`
    + ${this._manifest + ? html` + + + + ` + : nothing} + ${this._manifest?.config_flow || this._logInfo + ? html` + + ${this._manifest && + (this._manifest.is_built_in || this._manifest.issue_tracker) + ? html` + - - - ${this.hass.localize( - this._manifest.overwrites_built_in - ? "ui.panel.config.integrations.config_entry.custom_overwrites_core" - : "ui.panel.config.integrations.config_entry.custom_integration" - )} - -
    ` - : nothing} - ${this._manifest?.iot_class?.startsWith("cloud_") - ? html`
    - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.depends_on_cloud" + "ui.panel.config.integrations.config_entry.known_issues" )} -
    ` - : nothing} - ${normalEntries.length === 0 && - this._manifest && - !this._manifest.config_flow && - this.hass.config.components.find( - (comp) => comp.split(".")[0] === this.domain - ) - ? html`
    - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.no_config_flow" - )} -
    ` - : nothing} -
    - -
    - ${this._manifest?.is_built_in && - this._manifest.quality_scale && - Object.keys(QUALITY_SCALE_MAP).includes( - this._manifest.quality_scale - ) - ? html` - - - - ${this.hass.localize( - QUALITY_SCALE_MAP[this._manifest.quality_scale] - .translationKey - )} - - - - ` - : nothing} - ${devices.length > 0 - ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.${ - services ? "services" : "devices" - }`, - { count: devices.length } - )} - - - ` - : nothing} - ${numberOfEntities > 0 - ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.entities`, - { count: numberOfEntities } - )} - - - ` - : nothing} - ${this._manifest - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.documentation" - )} - - - - ` - : nothing} - ${this._manifest && - (this._manifest.is_built_in || this._manifest.issue_tracker) - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.known_issues" - )} - - - - ` - : nothing} - ${this._logInfo - ? html` - ${this._logInfo.level === LogSeverity.DEBUG - ? this.hass.localize( - "ui.panel.config.integrations.config_entry.disable_debug_logging" - ) - : this.hass.localize( - "ui.panel.config.integrations.config_entry.enable_debug_logging" - )} + - ` - : nothing} + + ` + : nothing} + ${this._logInfo + ? html` + ${this._logInfo.level === LogSeverity.DEBUG + ? this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_debug_logging" + ) + : this.hass.localize( + "ui.panel.config.integrations.config_entry.enable_debug_logging" + )} + + ` + : nothing} + ` + : nothing} + +
    +
    +
    +
    + ${domainToName(this.hass.localize,
    - +
    +

    ${domainToName(this.hass.localize, this.domain)}

    +
    + ${this._manifest?.is_built_in === false + ? html`` + : nothing} + ${this._logInfo?.level === LogSeverity.DEBUG + ? html`
    + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.debug_logging_enabled" + )} +
    ` + : nothing} + ${this._manifest?.iot_class?.startsWith("cloud_") + ? html`
    + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.depends_on_cloud" + )} +
    ` + : nothing} + ${normalEntries.length === 0 && + this._manifest && + !this._manifest.config_flow && + this.hass.config.components.find( + (comp) => comp.split(".")[0] === this.domain + ) + ? html`
    + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.no_config_flow" + )} +
    ` + : nothing} + ${this._manifest?.quality_scale && + Object.keys(QUALITY_SCALE_MAP).includes( + this._manifest.quality_scale + ) + ? html` + + ` + : nothing} +
    +
    + ${devices.length + ? html` + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.devices`, + { count: devices.length } + )} + + ` + : nothing} + ${devices.length && services.length ? " • " : ""} + ${services.length + ? html` + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.services`, + { count: services.length } + )} + ` + : nothing} + ${(devices.length || services.length) && numberOfEntities + ? " • " + : ""} + ${numberOfEntities + ? html` + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.entities`, + { count: numberOfEntities } + )} + + ` + : nothing} +
    +
    +
    +
    + + ${this._manifest?.integration_type + ? this.hass.localize( + `ui.panel.config.integrations.integration_page.add_${this._manifest.integration_type}` + ) + : this.hass.localize( + `ui.panel.config.integrations.integration_page.add_entry` + )} + + ${Array.from(supportedSubentryTypes).map( + (flowType) => + html` + + ${this.hass.localize( + `component.${this.domain}.config_subentries.${flowType}.initiate_flow.user` + )}` + )} +
    -
    - ${discoveryFlows.length - ? html` -

    + + ${discoveryFlows.length + ? html` +
    +

    ${this.hass.localize( "ui.panel.config.integrations.discovered" )} -

    - +

    + ${discoveryFlows.map( (flow) => html` @@ -578,106 +562,104 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ` )} -
    ` - : nothing} - ${attentionFlows.length || attentionEntries.length - ? html` -

    +

    + ` + : nothing} + ${attentionFlows.length || attentionEntries.length + ? html` +
    +

    ${this.hass.localize( `ui.panel.config.integrations.integration_page.attention_entries` )} -

    - - ${attentionFlows.map((flow) => { - const attention = ATTENTION_SOURCES.includes( - flow.context.source - ); - return html` - ${flow.localized_title} - ${this.hass.localize( - `ui.panel.config.integrations.${ - attention ? "attention" : "discovered" - }` - )} - - `; - })} - ${attentionEntries.map( - (item, index) => - html`${this._renderConfigEntry(item)} - ${index < attentionEntries.length - 1 - ? html` ` - : nothing} ` - )} - - ` - : nothing} + + ${attentionFlows.length + ? html` + ${attentionFlows.map((flow) => { + const attention = ATTENTION_SOURCES.includes( + flow.context.source + ); + return html` + ${flow.localized_title} + ${this.hass.localize( + `ui.panel.config.integrations.${ + attention ? "attention" : "discovered" + }` + )} + ${this.hass.localize( + `ui.panel.config.integrations.${ + attention ? "reconfigure" : "configure" + }` + )} + `; + })} + ` + : nothing} + ${attentionEntries.map( + (item) => + html`` + )} +
    + ` + : nothing} - -

    - ${this._manifest?.integration_type - ? this.hass.localize( - `ui.panel.config.integrations.integration_page.entries_${this._manifest.integration_type}` - ) - : this.hass.localize( - `ui.panel.config.integrations.integration_page.entries` - )} -

    - ${normalEntries.length === 0 - ? html`
    - ${this._manifest && - !this._manifest.config_flow && - this.hass.config.components.find( - (comp) => comp.split(".")[0] === this.domain - ) - ? this.hass.localize( - "ui.panel.config.integrations.integration_page.yaml_entry" - ) - : this.hass.localize( - "ui.panel.config.integrations.integration_page.no_entries" - )} -
    ` - : html` - ${normalEntries.map( - (item, index) => - html`${this._renderConfigEntry(item)} - ${index < normalEntries.length - 1 - ? html` ` - : nothing}` - )} - `} -
    - - ${this._manifest?.integration_type +
    +

    + ${this._manifest?.integration_type + ? this.hass.localize( + `ui.panel.config.integrations.integration_page.entries_${this._manifest.integration_type}` + ) + : this.hass.localize( + `ui.panel.config.integrations.integration_page.entries` + )} +

    + ${normalEntries.length === 0 + ? html`
    + ${this._manifest && + !this._manifest.config_flow && + this.hass.config.components.find( + (comp) => comp.split(".")[0] === this.domain + ) ? this.hass.localize( - `ui.panel.config.integrations.integration_page.add_${this._manifest.integration_type}` + "ui.panel.config.integrations.integration_page.yaml_entry" ) : this.hass.localize( - `ui.panel.config.integrations.integration_page.add_entry` + "ui.panel.config.integrations.integration_page.no_entries" )} - -
    - +
    ` + : html` + ${normalEntries.map( + (item) => + html`` + )} + `}
    @@ -692,421 +674,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ev.target.style.display = "none"; } - private _renderDeviceLine( - item: ConfigEntry, - devices: DeviceRegistryEntry[], - services: DeviceRegistryEntry[], - entities: EntityRegistryEntry[], - subItem?: SubEntry - ) { - let devicesLine: (TemplateResult | string)[] = []; - for (const [items, localizeKey] of [ - [devices, "devices"], - [services, "services"], - ] as const) { - if (items.length === 0) { - continue; - } - const url = - items.length === 1 - ? `/config/devices/device/${items[0].id}` - : `/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}${subItem ? `&sub_entry=${subItem.subentry_id}` : ""}`; - devicesLine.push( - // no white space before/after template on purpose - html`${this.hass.localize( - `ui.panel.config.integrations.config_entry.${localizeKey}`, - { count: items.length } - )}` - ); - } - - if (entities.length) { - devicesLine.push( - // no white space before/after template on purpose - html`${this.hass.localize( - "ui.panel.config.integrations.config_entry.entities", - { count: entities.length } - )}` - ); - } - - if (devicesLine.length === 0) { - devicesLine = [ - this.hass.localize( - "ui.panel.config.integrations.config_entry.no_devices_or_entities" - ), - ]; - } else if (devicesLine.length === 2) { - devicesLine = [ - devicesLine[0], - ` ${this.hass.localize("ui.common.and")} `, - devicesLine[1], - ]; - } else if (devicesLine.length === 3) { - devicesLine = [ - devicesLine[0], - ", ", - devicesLine[1], - ` ${this.hass.localize("ui.common.and")} `, - devicesLine[2], - ]; - } - return devicesLine; - } - - private _renderConfigEntry(item: ConfigEntry) { - let stateText: Parameters | undefined; - let stateTextExtra: TemplateResult | string | undefined; - let icon: string = mdiAlertCircle; - - if (!item.disabled_by && item.state === "not_loaded") { - stateText = ["ui.panel.config.integrations.config_entry.not_loaded"]; - } else if (item.state === "setup_in_progress") { - icon = mdiProgressHelper; - stateText = [ - "ui.panel.config.integrations.config_entry.setup_in_progress", - ]; - } else if (ERROR_STATES.includes(item.state)) { - if (item.state === "setup_retry") { - icon = mdiReloadAlert; - } - stateText = [ - `ui.panel.config.integrations.config_entry.state.${item.state}`, - ]; - stateTextExtra = renderConfigEntryError(this.hass, item); - } - - const devices = this._getConfigEntryDevices(item); - const services = this._getConfigEntryServices(item); - const entities = this._getConfigEntryEntities(item); - - let devicesLine: (TemplateResult | string)[] = []; - - if (item.disabled_by) { - devicesLine.push( - this.hass.localize( - "ui.panel.config.integrations.config_entry.disable.disabled_cause", - { - cause: - this.hass.localize( - `ui.panel.config.integrations.config_entry.disable.disabled_by.${item.disabled_by}` - ) || item.disabled_by, - } - ) - ); - if (item.state === "failed_unload") { - devicesLine.push(`. - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.disable_restart_confirm" - )}.`); - } - } else { - devicesLine = this._renderDeviceLine(item, devices, services, entities); - } - - const configPanel = this._configPanel(item.domain, this.hass.panels); - - const subEntries = this._subEntries[item.entry_id] || []; - - return html` -
    - ${item.title || domainToName(this.hass.localize, item.domain)} -
    -
    -
    ${devicesLine}
    - ${stateText - ? html` -
    - -
    - ${this.hass.localize(...stateText)}${stateTextExtra - ? html`: ${stateTextExtra}` - : nothing} -
    -
    - ` - : nothing} -
    - ${item.disabled_by === "user" - ? html` - ${this.hass.localize("ui.common.enable")} - ` - : configPanel && - (item.domain !== "matter" || - isDevVersion(this.hass.config.version)) && - !stateText - ? html` - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.configure" - )} - ` - : item.supports_options - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.configure" - )} - - ` - : nothing} - - - ${item.disabled_by && devices.length - ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.devices`, - { count: devices.length } - )} - - - ` - : nothing} - ${item.disabled_by && services.length - ? html` - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.services`, - { count: services.length } - )} - - ` - : nothing} - ${item.disabled_by && entities.length - ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.entities`, - { count: entities.length } - )} - - - ` - : nothing} - ${!item.disabled_by && - RECOVERABLE_STATES.includes(item.state) && - item.supports_unload && - item.source !== "system" - ? html` - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.reload" - )} - - ` - : nothing} - - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.rename" - )} - - - ${Object.keys(item.supported_subentry_types).map( - (flowType) => - html` - - ${this.hass.localize( - `component.${item.domain}.config_subentries.${flowType}.initiate_flow.user` - )}` - )} - - - - ${this._diagnosticHandler && item.state === "loaded" - ? html` - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.download_diagnostics" - )} - - ` - : nothing} - ${!item.disabled_by && - item.supports_reconfigure && - item.source !== "system" - ? html` - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.reconfigure" - )} - - ` - : nothing} - - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.system_options" - )} - - ${item.disabled_by === "user" - ? html` - - - ${this.hass.localize("ui.common.enable")} - - ` - : item.source !== "system" - ? html` - - - ${this.hass.localize("ui.common.disable")} - - ` - : nothing} - ${item.source !== "system" - ? html` - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.delete" - )} - - ` - : nothing} - -
    - ${subEntries.map((subEntry) => this._renderSubEntry(item, subEntry))}`; - } - - private _renderSubEntry(configEntry: ConfigEntry, subEntry: SubEntry) { - const devices = this._getConfigEntryDevices(configEntry).filter((device) => - device.config_entries_subentries[configEntry.entry_id]?.includes( - subEntry.subentry_id - ) - ); - const services = this._getConfigEntryServices(configEntry).filter( - (device) => - device.config_entries_subentries[configEntry.entry_id]?.includes( - subEntry.subentry_id - ) - ); - const entities = this._getConfigEntryEntities(configEntry).filter( - (entity) => entity.config_subentry_id === subEntry.subentry_id - ); - - return html` - ${subEntry.title} - ${this.hass.localize( - `component.${configEntry.domain}.config_subentries.${subEntry.subentry_type}.entry_type` - )} - - - ${this._renderDeviceLine( - configEntry, - devices, - services, - entities, - subEntry - )} - ${configEntry.supported_subentry_types[subEntry.subentry_type] - ?.supports_reconfigure - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.configure" - )} - - ` - : nothing} - - - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.delete" - )} - - - `; - } - private async _highlightEntry() { await nextRender(); const entryId = this._searchParms.get("config_entry")!; @@ -1146,27 +713,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { } } - private async _fetchSubEntries() { - const subEntriesPromises = ( - this._extraConfigEntries || this.configEntries - )?.map((entry) => - entry.num_subentries - ? getSubEntries(this.hass, entry.entry_id).then((subEntries) => ({ - entry_id: entry.entry_id, - subEntries, - })) - : undefined - ); - if (subEntriesPromises) { - const subEntries = await Promise.all(subEntriesPromises); - this._subEntries = {}; - subEntries.forEach((entry) => { - if (!entry) return; - this._subEntries[entry.entry_id] = entry.subEntries; - }); - } - } - private async _fetchDiagnostics() { if (!this.domain || !isComponentLoaded(this.hass, "diagnostics")) { return; @@ -1239,361 +785,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { } ); - private _getConfigEntryEntities = ( - configEntry: ConfigEntry - ): EntityRegistryEntry[] => { - const entries = this._domainConfigEntries( - this.domain, - this._extraConfigEntries || this.configEntries - ); - const entityRegistryEntries = this._getEntities(entries, this._entities); - return entityRegistryEntries.filter( - (entity) => entity.config_entry_id === configEntry.entry_id - ); - }; - - private _getConfigEntryDevices = ( - configEntry: ConfigEntry - ): DeviceRegistryEntry[] => { - const entries = this._domainConfigEntries( - this.domain, - this._extraConfigEntries || this.configEntries - ); - const deviceRegistryEntries = this._getDevices(entries, this.hass.devices); - return Object.values(deviceRegistryEntries).filter( - (device) => - device.config_entries.includes(configEntry.entry_id) && - device.entry_type !== "service" - ); - }; - - private _getConfigEntryServices = ( - configEntry: ConfigEntry - ): DeviceRegistryEntry[] => { - const entries = this._domainConfigEntries( - this.domain, - this._extraConfigEntries || this.configEntries - ); - const deviceRegistryEntries = this._getDevices(entries, this.hass.devices); - return Object.values(deviceRegistryEntries).filter( - (device) => - device.config_entries.includes(configEntry.entry_id) && - device.entry_type === "service" - ); - }; - - private _showOptions(ev) { - showOptionsFlowDialog( - this, - ev.target.closest(".config_entry").configEntry, - { manifest: this._manifest } - ); - } - - private _handleRename(ev: Event): void { - this._editEntryName( - ((ev.target as HTMLElement).closest(".config_entry") as any).configEntry - ); - } - - private _handleReload(ev: Event): void { - this._reloadIntegration( - ((ev.target as HTMLElement).closest(".config_entry") as any).configEntry - ); - } - - private _handleReconfigure(ev: Event): void { - this._reconfigureIntegration( - ((ev.target as HTMLElement).closest(".config_entry") as any).configEntry - ); - } - - private _handleDelete(ev: Event): void { - this._removeIntegration( - ((ev.target as HTMLElement).closest(".config_entry") as any).configEntry - ); - } - - private async _handleReconfigureSub(ev: Event): Promise { - const configEntry = ( - (ev.target as HTMLElement).closest(".sub-entry") as any - ).configEntry; - const subEntry = ((ev.target as HTMLElement).closest(".sub-entry") as any) - .subEntry; - - showSubConfigFlowDialog( - this, - configEntry, - subEntry.flowType || subEntry.subentry_type, - { - startFlowHandler: configEntry.entry_id, - subEntryId: subEntry.subentry_id, - } - ); - } - - private async _handleDeleteSub(ev: Event): Promise { - const configEntry = ( - (ev.target as HTMLElement).closest(".sub-entry") as any - ).configEntry; - const subEntry = ((ev.target as HTMLElement).closest(".sub-entry") as any) - .subEntry; - const confirmed = await showConfirmationDialog(this, { - title: this.hass.localize( - "ui.panel.config.integrations.config_entry.delete_confirm_title", - { title: subEntry.title } - ), - text: this.hass.localize( - "ui.panel.config.integrations.config_entry.delete_confirm_text" - ), - confirmText: this.hass!.localize("ui.common.delete"), - dismissText: this.hass!.localize("ui.common.cancel"), - destructive: true, - }); - - if (!confirmed) { - return; - } - await deleteSubEntry(this.hass, configEntry.entry_id, subEntry.subentry_id); - } - - private _handleDisable(ev: Event): void { - this._disableIntegration( - ((ev.target as HTMLElement).closest(".config_entry") as any).configEntry - ); - } - - private _handleEnable(ev: Event): void { - this._enableIntegration( - ((ev.target as HTMLElement).closest(".config_entry") as any).configEntry - ); - } - - private _handleSystemOptions(ev: Event): void { - this._showSystemOptions( - ((ev.target as HTMLElement).closest(".config_entry") as any).configEntry - ); - } - - private _showSystemOptions(configEntry: ConfigEntry) { - showConfigEntrySystemOptionsDialog(this, { - entry: configEntry, - manifest: this._manifest, - }); - } - - private async _disableIntegration(configEntry: ConfigEntry) { - const entryId = configEntry.entry_id; - - const confirmed = await showConfirmationDialog(this, { - title: this.hass.localize( - "ui.panel.config.integrations.config_entry.disable_confirm_title", - { title: configEntry.title } - ), - text: this.hass.localize( - "ui.panel.config.integrations.config_entry.disable_confirm_text" - ), - confirmText: this.hass!.localize("ui.common.disable"), - dismissText: this.hass!.localize("ui.common.cancel"), - destructive: true, - }); - - if (!confirmed) { - return; - } - let result: DisableConfigEntryResult; - try { - result = await disableConfigEntry(this.hass, entryId); - } catch (err: any) { - showAlertDialog(this, { - title: this.hass.localize( - "ui.panel.config.integrations.config_entry.disable_error" - ), - text: err.message, - }); - return; - } - if (result.require_restart) { - showAlertDialog(this, { - text: this.hass.localize( - "ui.panel.config.integrations.config_entry.disable_restart_confirm" - ), - }); - } - } - - private async _enableIntegration(configEntry: ConfigEntry) { - const entryId = configEntry.entry_id; - - let result: DisableConfigEntryResult; - try { - result = await enableConfigEntry(this.hass, entryId); - } catch (err: any) { - showAlertDialog(this, { - title: this.hass.localize( - "ui.panel.config.integrations.config_entry.disable_error" - ), - text: err.message, - }); - return; - } - - if (result.require_restart) { - showAlertDialog(this, { - text: this.hass.localize( - "ui.panel.config.integrations.config_entry.enable_restart_confirm" - ), - }); - } - } - - private async _removeIntegration(configEntry: ConfigEntry) { - const entryId = configEntry.entry_id; - - const applicationCredentialsId = - await this._applicationCredentialForRemove(entryId); - - const confirmed = await showConfirmationDialog(this, { - title: this.hass.localize( - "ui.panel.config.integrations.config_entry.delete_confirm_title", - { title: configEntry.title } - ), - text: this.hass.localize( - "ui.panel.config.integrations.config_entry.delete_confirm_text" - ), - confirmText: this.hass!.localize("ui.common.delete"), - dismissText: this.hass!.localize("ui.common.cancel"), - destructive: true, - }); - - if (!confirmed) { - return; - } - const result = await deleteConfigEntry(this.hass, entryId); - - if (result.require_restart) { - showAlertDialog(this, { - text: this.hass.localize( - "ui.panel.config.integrations.config_entry.restart_confirm" - ), - }); - } - if (applicationCredentialsId) { - this._removeApplicationCredential(applicationCredentialsId); - } - } - - // Return an application credentials id for this config entry to prompt the - // user for removal. This is best effort so we don't stop overall removal - // if the integration isn't loaded or there is some other error. - private async _applicationCredentialForRemove(entryId: string) { - try { - return (await fetchApplicationCredentialsConfigEntry(this.hass, entryId)) - .application_credentials_id; - } catch (_err: any) { - // We won't prompt the user to remove credentials - return null; - } - } - - private async _removeApplicationCredential(applicationCredentialsId: string) { - const confirmed = await showConfirmationDialog(this, { - title: this.hass.localize( - "ui.panel.config.integrations.config_entry.application_credentials.delete_title" - ), - text: html`${this.hass.localize( - "ui.panel.config.integrations.config_entry.application_credentials.delete_prompt" - )}, -
    -
    - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.application_credentials.delete_detail" - )} -
    -
    - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.application_credentials.learn_more" - )} - `, - destructive: true, - confirmText: this.hass.localize("ui.common.remove"), - dismissText: this.hass.localize( - "ui.panel.config.integrations.config_entry.application_credentials.dismiss" - ), - }); - if (!confirmed) { - return; - } - try { - await deleteApplicationCredential(this.hass, applicationCredentialsId); - } catch (err: any) { - showAlertDialog(this, { - title: this.hass.localize( - "ui.panel.config.integrations.config_entry.application_credentials.delete_error_title" - ), - text: err.message, - }); - } - } - - private async _reloadIntegration(configEntry: ConfigEntry) { - const entryId = configEntry.entry_id; - - const result = await reloadConfigEntry(this.hass, entryId); - const locale_key = result.require_restart - ? "reload_restart_confirm" - : "reload_confirm"; - showAlertDialog(this, { - text: this.hass.localize( - `ui.panel.config.integrations.config_entry.${locale_key}` - ), - }); - } - - private async _reconfigureIntegration(configEntry: ConfigEntry) { - showConfigFlowDialog(this, { - startFlowHandler: configEntry.domain, - showAdvanced: this.hass.userData?.showAdvanced, - manifest: await fetchIntegrationManifest(this.hass, configEntry.domain), - entryId: configEntry.entry_id, - navigateToResult: true, - }); - } - - private async _editEntryName(configEntry: ConfigEntry) { - const newName = await showPromptDialog(this, { - title: this.hass.localize("ui.panel.config.integrations.rename_dialog"), - defaultValue: configEntry.title, - inputLabel: this.hass.localize( - "ui.panel.config.integrations.rename_input_label" - ), - }); - if (newName === null) { - return; - } - await updateConfigEntry(this.hass, configEntry.entry_id, { - title: newName, - }); - } - - private async _signUrl(ev) { - const anchor = ev.currentTarget; - ev.preventDefault(); - const signedUrl = await getSignedPath( - this.hass, - anchor.getAttribute("href") - ); - fileDownload(signedUrl.path); - } - private async _addIntegration() { if (!this._manifest?.config_flow) { showAlertDialog(this, { @@ -1636,8 +827,33 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { } private async _addSubEntry(ev) { - showSubConfigFlowDialog(this, ev.target.entry, ev.target.flowType, { - startFlowHandler: ev.target.entry.entry_id, + const flowType = ev.target.flowType; + + const configEntries = this._domainConfigEntries( + this.domain, + this._extraConfigEntries || this.configEntries + ).filter((entry) => entry.source !== "ignore"); + + if (!configEntries.length) { + return; + } + + if (configEntries.length === 1 && configEntries[0].state === "loaded") { + showSubConfigFlowDialog(this, configEntries[0], flowType, { + startFlowHandler: configEntries[0].entry_id, + }); + return; + } + + showPickConfigEntryDialog(this, { + domain: this.domain, + subFlowType: flowType, + configEntries, + configEntryPicked: (entry) => { + showSubConfigFlowDialog(this, entry, flowType, { + startFlowHandler: entry.entry_id, + }); + }, }); } @@ -1650,35 +866,49 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { flex-wrap: wrap; margin: auto; max-width: 1000px; - margin-top: 32px; - margin-bottom: 32px; + padding: 32px; + } + .container > * { + flex-grow: 1; + } + .header { + display: flex; + flex-wrap: wrap; + gap: 24px; + align-items: center; + justify-content: space-between; + margin-bottom: 24px; + } + .title-container { + display: flex; + align-items: center; + } + .title { + display: flex; + gap: 8px; + flex-direction: column; + justify-content: space-between; + } + .title h1 { + font-family: Roboto; + font-size: 32px; + font-weight: 700; + line-height: 40px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + margin: 0; + } + .sub { + display: flex; + flex-wrap: wrap; + gap: 8px 16px; } .card-content { padding: 16px 0 8px; } - .column { - width: 33%; - flex-grow: 1; - } - .column.small { - max-width: 300px; - } - .column, - .fullwidth { - padding: 8px; - box-sizing: border-box; - } - .column > *:not(:first-child) { - margin-top: 16px; - } - - :host([narrow]) .column { - width: 100%; - max-width: unset; - } - :host([narrow]) .container { - margin-top: 0; + padding: 16px; } .card-header { padding-bottom: 0; @@ -1689,7 +919,11 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { .logo-container { display: flex; justify-content: center; - margin-bottom: 8px; + margin-right: 16px; + padding: 0 8px; + } + .logo-container img { + width: 80px; } .version { padding-top: 8px; @@ -1710,12 +944,31 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { mask-position: left; } } + .actions { + display: flex; + gap: 8px; + } + .section { + width: 100%; + } + .section-header { + margin-inline-start: 16px; + margin-top: 6px; + margin-bottom: 6px; + font-family: Roboto; + font-size: 14px; + font-weight: 500; + line-height: 20px; + letter-spacing: 0.10000000149011612px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: var(--secondary-text-color); + } .integration-info { display: flex; align-items: center; - gap: 20px; - padding: 0 20px; - min-height: 48px; + gap: 8px; } .integration-info ha-svg-icon { min-width: 24px; @@ -1754,7 +1007,26 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { color: var(--mdc-theme-text-icon-on-background, rgba(0, 0, 0, 0.38)); animation: unset; } + ha-md-list { + border: 1px solid var(--divider-color); + border-radius: 8px; + padding: 0; + } + .discovered { + --md-list-container-color: rgba(var(--rgb-success-color), 0.2); + } + .discovered ha-button { + --mdc-theme-primary: var(--success-color); + } + .attention { + --md-list-container-color: rgba(var(--rgb-warning-color), 0.2); + } + .attention ha-button { + --mdc-theme-primary: var(--warning-color); + } ha-md-list-item { + --md-list-item-top-space: 4px; + --md-list-item-bottom-space: 4px; position: relative; } ha-md-list-item.discovered { @@ -1770,8 +1042,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { pointer-events: none; content: ""; } - ha-md-list-item.sub-entry { - --md-list-item-leading-space: 50px; + ha-config-entry-row { + display: block; + margin-bottom: 16px; } a { text-decoration: none; @@ -1779,19 +1052,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { .highlight::after { background-color: var(--info-color); } - .attention { - primary-color: var(--error-color); - } .warning { color: var(--error-color); } - .state-error { - --state-message-color: var(--error-color); - --text-on-state-color: var(--text-primary-color); - } - .state-error::after { - background-color: var(--error-color); - } .state-failed-unload { --state-message-color: var(--warning-color); --text-on-state-color: var(--primary-text-color); @@ -1837,6 +1100,13 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { margin-top: 8px; margin-bottom: 8px; } + a[slot="toolbar-icon"] { + color: var(--sidebar-icon-color); + } + ha-svg-icon.open-external { + min-width: 14px; + width: 14px; + } `, ]; } diff --git a/src/panels/config/integrations/ha-config-sub-entry-row.ts b/src/panels/config/integrations/ha-config-sub-entry-row.ts new file mode 100644 index 0000000000..c30825a6c1 --- /dev/null +++ b/src/panels/config/integrations/ha-config-sub-entry-row.ts @@ -0,0 +1,250 @@ +import { + mdiChevronDown, + mdiChevronUp, + mdiCogOutline, + mdiDelete, + mdiDevices, + mdiDotsVertical, + mdiHandExtendedOutline, + mdiShapeOutline, +} from "@mdi/js"; +import { css, html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import type { ConfigEntry, SubEntry } from "../../../data/config_entries"; +import { deleteSubEntry } from "../../../data/config_entries"; +import type { DeviceRegistryEntry } from "../../../data/device_registry"; +import type { DiagnosticInfo } from "../../../data/diagnostics"; +import type { EntityRegistryEntry } from "../../../data/entity_registry"; +import type { IntegrationManifest } from "../../../data/integration"; +import { showSubConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-sub-config-flow"; +import type { HomeAssistant } from "../../../types"; +import { showConfirmationDialog } from "../../lovelace/custom-card-helpers"; +import "./ha-config-entry-device-row"; + +@customElement("ha-config-sub-entry-row") +class HaConfigSubEntryRow extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean, reflect: true }) public narrow = false; + + @property({ attribute: false }) public manifest?: IntegrationManifest; + + @property({ attribute: false }) public diagnosticHandler?: DiagnosticInfo; + + @property({ attribute: false }) public entities!: EntityRegistryEntry[]; + + @property({ attribute: false }) public entry!: ConfigEntry; + + @property({ attribute: false }) public subEntry!: SubEntry; + + @state() private _expanded = true; + + protected render() { + const subEntry = this.subEntry; + const configEntry = this.entry; + + const devices = this._getDevices(); + const services = this._getServices(); + const entities = this._getEntities(); + + return html` + + ${devices.length || services.length + ? html`` + : nothing} + ${subEntry.title} + ${this.hass.localize( + `component.${configEntry.domain}.config_subentries.${subEntry.subentry_type}.entry_type` + )} + ${configEntry.supported_subentry_types[subEntry.subentry_type] + ?.supports_reconfigure + ? html` + + + ` + : nothing} + + + ${devices.length || services.length + ? html` + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.devices`, + { count: devices.length } + )} + + + ` + : nothing} + ${services.length + ? html` + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.services`, + { count: services.length } + )} + + ` + : nothing} + ${entities.length + ? html` + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.entities`, + { count: entities.length } + )} + + + ` + : nothing} + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.delete" + )} + + + + ${this._expanded + ? html` + ${devices.map( + (device) => + html`` + )} + ` + : nothing} + `; + } + + private _toggleExpand() { + this._expanded = !this._expanded; + } + + private _getEntities = (): EntityRegistryEntry[] => + this.entities.filter( + (entity) => entity.config_subentry_id === this.subEntry.subentry_id + ); + + private _getDevices = (): DeviceRegistryEntry[] => + Object.values(this.hass.devices).filter( + (device) => + device.config_entries_subentries[this.entry.entry_id]?.includes( + this.subEntry.subentry_id + ) && device.entry_type !== "service" + ); + + private _getServices = (): DeviceRegistryEntry[] => + Object.values(this.hass.devices).filter( + (device) => + device.config_entries_subentries[this.entry.entry_id]?.includes( + this.subEntry.subentry_id + ) && device.entry_type === "service" + ); + + private async _handleReconfigureSub(): Promise { + showSubConfigFlowDialog(this, this.entry, this.subEntry.subentry_type, { + startFlowHandler: this.entry.entry_id, + subEntryId: this.subEntry.subentry_id, + }); + } + + private async _handleDeleteSub(): Promise { + const confirmed = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.delete_confirm_title", + { title: this.subEntry.title } + ), + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.delete_confirm_text" + ), + confirmText: this.hass!.localize("ui.common.delete"), + dismissText: this.hass!.localize("ui.common.cancel"), + destructive: true, + }); + + if (!confirmed) { + return; + } + await deleteSubEntry( + this.hass, + this.entry.entry_id, + this.subEntry.subentry_id + ); + } + + static styles = css` + .expand-button { + margin: 0 -12px; + } + ha-md-list { + border: 1px solid var(--divider-color); + border-radius: var(--ha-card-border-radius, 12px); + padding: 0; + margin: 16px; + margin-top: 0; + } + ha-md-list-item.has-subentries { + border-bottom: 1px solid var(--divider-color); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-sub-entry-row": HaConfigSubEntryRow; + } +} diff --git a/src/panels/config/integrations/show-pick-config-entry-dialog.ts b/src/panels/config/integrations/show-pick-config-entry-dialog.ts new file mode 100644 index 0000000000..0bd3fbd604 --- /dev/null +++ b/src/panels/config/integrations/show-pick-config-entry-dialog.ts @@ -0,0 +1,20 @@ +import { fireEvent } from "../../../common/dom/fire_event"; +import type { ConfigEntry } from "../../../data/config_entries"; + +export interface PickConfigEntryDialogParams { + domain: string; + subFlowType: string; + configEntries: ConfigEntry[]; + configEntryPicked: (configEntry: ConfigEntry) => void; +} + +export const showPickConfigEntryDialog = ( + element: HTMLElement, + dialogParams?: PickConfigEntryDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-pick-config-entry", + dialogImport: () => import("./dialog-pick-config-entry"), + dialogParams: dialogParams, + }); +}; diff --git a/src/resources/theme/color.globals.ts b/src/resources/theme/color.globals.ts index e80932117e..56ceb72acb 100644 --- a/src/resources/theme/color.globals.ts +++ b/src/resources/theme/color.globals.ts @@ -31,6 +31,11 @@ export const colorStyles = css` --rgb-text-primary-color: 255, 255, 255; --rgb-card-background-color: 255, 255, 255; + --rgb-warning-color: 255, 166, 0; + --rgb-error-color: 219, 68, 55; + --rgb-success-color: 67, 160, 71; + --rgb-info-color: 3, 155, 229; + --scrollbar-thumb-color: rgb(194, 194, 194); --error-color: #db4437; diff --git a/src/translations/en.json b/src/translations/en.json index 028dd1a6f6..3632d746f5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5319,11 +5319,18 @@ "dismiss": "Keep", "learn_more": "Learn more about application credentials" }, + "device": { + "enable": "Enable device", + "disable": "Disable device", + "configure": "Configure device", + "delete": "Remove device" + }, "devices": "{count} {count, plural,\n one {device}\n other {devices}\n}", "entities": "{count} {count, plural,\n one {entity}\n other {entities}\n}", "services": "{count} {count, plural,\n one {service}\n other {services}\n}", "entries": "{count} {count, plural,\n one {entry}\n other {entries}\n}", "no_devices_or_entities": "No devices or entities", + "devices_without_subentry": "Devices that don't belong to a sub-entry", "rename": "Rename", "configure": "Configure", "system_options": "System options", From 508e451f94e0c411aa17f86437f892e4989876da Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 24 Jun 2025 15:27:52 +0200 Subject: [PATCH 104/216] Add container arch to system health data (#25896) --- src/data/system_health.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/system_health.ts b/src/data/system_health.ts index d8fb40c924..2723dc9fe7 100644 --- a/src/data/system_health.ts +++ b/src/data/system_health.ts @@ -34,6 +34,7 @@ export type SystemHealthInfo = Partial<{ dev: boolean; hassio: boolean; docker: boolean; + container_arch: string; user: string; virtualenv: boolean; python_version: string; From edcca81acc299096a6f99a2df0e458cef8ab0adf Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 24 Jun 2025 15:28:46 +0200 Subject: [PATCH 105/216] Group area per floor in the areas strategy editor (#25895) --- .../ha-areas-floors-display-editor.ts | 219 ++++++++++++++++++ .../areas/areas-overview-view-strategy.ts | 13 +- .../hui-areas-dashboard-strategy-editor.ts | 5 +- src/translations/en.json | 3 +- 4 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 src/components/ha-areas-floors-display-editor.ts diff --git a/src/components/ha-areas-floors-display-editor.ts b/src/components/ha-areas-floors-display-editor.ts new file mode 100644 index 0000000000..414e2550c7 --- /dev/null +++ b/src/components/ha-areas-floors-display-editor.ts @@ -0,0 +1,219 @@ +import { mdiTextureBox } from "@mdi/js"; +import type { TemplateResult } from "lit"; +import { LitElement, css, html } from "lit"; +import { customElement, property } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../common/dom/fire_event"; +import { computeFloorName } from "../common/entity/compute_floor_name"; +import { getAreaContext } from "../common/entity/context/get_area_context"; +import { stringCompare } from "../common/string/compare"; +import { areaCompare } from "../data/area_registry"; +import type { FloorRegistryEntry } from "../data/floor_registry"; +import type { HomeAssistant } from "../types"; +import "./ha-expansion-panel"; +import "./ha-floor-icon"; +import "./ha-items-display-editor"; +import type { DisplayItem, DisplayValue } from "./ha-items-display-editor"; +import "./ha-svg-icon"; +import "./ha-textfield"; + +export interface AreasDisplayValue { + hidden?: string[]; + order?: string[]; +} + +const UNASSIGNED_FLOOR = "__unassigned__"; + +@customElement("ha-areas-floors-display-editor") +export class HaAreasFloorsDisplayEditor extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public label?: string; + + @property({ attribute: false }) public value?: AreasDisplayValue; + + @property() public helper?: string; + + @property({ type: Boolean }) public expanded = false; + + @property({ type: Boolean }) public disabled = false; + + @property({ type: Boolean }) public required = false; + + @property({ type: Boolean, attribute: "show-navigation-button" }) + public showNavigationButton = false; + + protected render(): TemplateResult { + const groupedItems = this._groupedItems(this.hass.areas, this.hass.floors); + + const filteredFloors = this._sortedFloors(this.hass.floors).filter( + (floor) => + // Only include floors that have areas assigned to them + groupedItems[floor.floor_id]?.length > 0 + ); + + const value: DisplayValue = { + order: this.value?.order ?? [], + hidden: this.value?.hidden ?? [], + }; + + return html` + + + ${filteredFloors.map( + (floor) => html` +
    +
    + +

    ${computeFloorName(floor)}

    +
    +
    + +
    +
    + ` + )} +
    + `; + } + + private _groupedItems = memoizeOne( + ( + hassAreas: HomeAssistant["areas"], + // update items if floors change + _hassFloors: HomeAssistant["floors"] + ): Record => { + const compare = areaCompare(hassAreas); + + const areas = Object.values(hassAreas).sort((areaA, areaB) => + compare(areaA.area_id, areaB.area_id) + ); + const groupedItems: Record = areas.reduce( + (acc, area) => { + const { floor } = getAreaContext(area, this.hass!); + const floorId = floor?.floor_id ?? UNASSIGNED_FLOOR; + + if (!acc[floorId]) { + acc[floorId] = []; + } + acc[floorId].push({ + value: area.area_id, + label: area.name, + icon: area.icon ?? undefined, + iconPath: mdiTextureBox, + description: floor?.name, + }); + + return acc; + }, + {} as Record + ); + return groupedItems; + } + ); + + private _sortedFloors = memoizeOne( + (hassFloors: HomeAssistant["floors"]): FloorRegistryEntry[] => { + const floors = Object.values(hassFloors).sort((floorA, floorB) => { + if (floorA.level !== floorB.level) { + return (floorA.level ?? 0) - (floorB.level ?? 0); + } + return stringCompare(floorA.name, floorB.name); + }); + floors.push({ + floor_id: UNASSIGNED_FLOOR, + name: this.hass.localize( + "ui.panel.lovelace.strategy.areas.unassigned_areas" + ), + icon: null, + level: 999999, + aliases: [], + created_at: 0, + modified_at: 0, + }); + return floors; + } + ); + + private async _areaDisplayChanged(ev) { + ev.stopPropagation(); + const value = ev.detail.value as DisplayValue; + const currentFloorId = ev.currentTarget.floorId; + + const floorIds = this._sortedFloors(this.hass.floors).map( + (floor) => floor.floor_id + ); + + const newHidden: string[] = []; + const newOrder: string[] = []; + + for (const floorId of floorIds) { + if (currentFloorId === floorId) { + newHidden.push(...(value.hidden ?? [])); + newOrder.push(...(value.order ?? [])); + continue; + } + const hidden = this.value?.hidden?.filter( + (areaId) => this.hass.areas[areaId]?.floor_id === floorId + ); + if (hidden) { + newHidden.push(...hidden); + } + const order = this.value?.order?.filter( + (areaId) => this.hass.areas[areaId]?.floor_id === floorId + ); + if (order) { + newOrder.push(...order); + } + } + + const newValue: AreasDisplayValue = { + hidden: newHidden, + order: newOrder, + }; + if (newValue.hidden?.length === 0) { + delete newValue.hidden; + } + if (newValue.order?.length === 0) { + delete newValue.order; + } + + fireEvent(this, "value-changed", { value: newValue }); + } + + static styles = css` + .floor .header p { + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + flex: 1: + } + .floor .header { + margin: 16px 0 8px 0; + padding: 0 8px; + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-areas-floors-display-editor": HaAreasFloorsDisplayEditor; + } +} diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 74bbd0a38c..230b2230fe 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -11,6 +11,8 @@ import type { AreaCardConfig, HeadingCardConfig } from "../../cards/types"; import type { EntitiesDisplay } from "./area-view-strategy"; import { computeAreaPath, getAreas } from "./helpers/areas-strategy-helper"; +const UNASSIGNED_FLOOR = "__unassigned__"; + interface AreaOptions { groups_options?: Record; } @@ -46,13 +48,20 @@ export class AreasOverviewViewStrategy extends ReactiveElement { const floorSections = [ ...floors, - { floor_id: "default", name: "Default", level: null, icon: null }, + { + floor_id: UNASSIGNED_FLOOR, + name: hass.localize( + "ui.panel.lovelace.strategy.areas.unassigned_areas" + ), + level: null, + icon: null, + }, ] .map((floor) => { const areasInFloors = areas.filter( (area) => area.floor_id === floor.floor_id || - (!area.floor_id && floor.floor_id === "default") + (!area.floor_id && floor.floor_id === UNASSIGNED_FLOOR) ); if (areasInFloors.length === 0) { diff --git a/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts b/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts index 6edf10b22c..311641caa2 100644 --- a/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts +++ b/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts @@ -22,6 +22,7 @@ import { type AreaRegistryEntry, } from "../../../../../data/area_registry"; import { buttonLinkStyle } from "../../../../../resources/styles"; +import "../../../../../components/ha-areas-floors-display-editor"; @customElement("hui-areas-dashboard-strategy-editor") export class HuiAreasDashboardStrategyEditor @@ -122,7 +123,7 @@ export class HuiAreasDashboardStrategyEditor const value = this._config.areas_display; return html` - + > `; } diff --git a/src/translations/en.json b/src/translations/en.json index 3632d746f5..009e1806c7 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6625,7 +6625,8 @@ "security": "Security", "actions": "Actions", "others": "Others" - } + }, + "unassigned_areas": "[%key:ui::panel::config::areas::picker::unassigned_areas%]" } }, "cards": { From caa60e4e8c3ac430d53ff615e341ab08d03daeb8 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 24 Jun 2025 18:02:45 +0200 Subject: [PATCH 106/216] Fix warnings raised when migrating incomplete automation configuration (#25898) --- src/data/script.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/script.ts b/src/data/script.ts index 9faf0b4127..6128014dfc 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -415,7 +415,7 @@ export const migrateAutomationAction = ( return action.map(migrateAutomationAction) as Action[]; } - if ("service" in action) { + if (typeof action === "object" && action !== null && "service" in action) { if (!("action" in action)) { action.action = action.service; } @@ -423,7 +423,7 @@ export const migrateAutomationAction = ( } // legacy scene (scene: scene_name) - if ("scene" in action) { + if (typeof action === "object" && action !== null && "scene" in action) { action.action = "scene.turn_on"; action.target = { entity_id: action.scene, @@ -431,7 +431,7 @@ export const migrateAutomationAction = ( delete action.scene; } - if ("sequence" in action) { + if (typeof action === "object" && action !== null && "sequence" in action) { for (const sequenceAction of (action as SequenceAction).sequence) { migrateAutomationAction(sequenceAction); } From 31bc708725a83c10d89957ac0d3e5df1e6a62249 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 24 Jun 2025 18:09:56 +0200 Subject: [PATCH 107/216] Improve alerts padding in area card (#25897) --- src/panels/lovelace/cards/hui-area-card.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index f91dc6bec0..3f87f13f65 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -663,8 +663,8 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { left: 0; display: flex; flex-direction: row; - gap: 4px; - padding: 4px; + gap: 8px; + padding: 8px; pointer-events: none; z-index: 1; } From 35ba2fffda2dd3f7e5bdf4e817e8bfc8c2fd17d8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 24 Jun 2025 13:46:47 -0400 Subject: [PATCH 108/216] Also show sub entry services when sub entry expanded (#25900) --- .../config/integrations/ha-config-sub-entry-row.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/panels/config/integrations/ha-config-sub-entry-row.ts b/src/panels/config/integrations/ha-config-sub-entry-row.ts index c30825a6c1..29d832ca5f 100644 --- a/src/panels/config/integrations/ha-config-sub-entry-row.ts +++ b/src/panels/config/integrations/ha-config-sub-entry-row.ts @@ -165,6 +165,16 @@ class HaConfigSubEntryRow extends LitElement { .entities=${this.entities} >` )} + ${services.map( + (service) => + html`` + )} ` : nothing} `; From 3aafa47f6d442482232b1e3ef14dfd400634e18c Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 24 Jun 2025 22:22:31 +0200 Subject: [PATCH 109/216] Improve Entity ID auto-complete in YAML mode (#25901) --- src/components/ha-code-editor.ts | 120 +++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index 17003c8e0b..190b456fbd 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -257,6 +257,126 @@ export class HaCodeEditor extends ReactiveElement { private _entityCompletions( context: CompletionContext ): CompletionResult | null | Promise { + // Check for YAML mode and entity-related fields + if (this.mode === "yaml") { + const currentLine = context.state.doc.lineAt(context.pos); + const lineText = currentLine.text; + + // Properties that commonly contain entity IDs + const entityProperties = [ + "entity_id", + "entity", + "entities", + "badges", + "devices", + "lights", + "light", + "group_members", + "scene", + "zone", + "zones", + ]; + + // Create regex pattern for all entity properties + const propertyPattern = entityProperties.join("|"); + const entityFieldRegex = new RegExp( + `^\\s*(-\\s+)?(${propertyPattern}):\\s*` + ); + + // Check if we're in an entity field (single entity or list item) + const entityFieldMatch = lineText.match(entityFieldRegex); + const listItemMatch = lineText.match(/^\s*-\s+/); + + if (entityFieldMatch) { + // Calculate the position after the entity field + const afterField = currentLine.from + entityFieldMatch[0].length; + + // If cursor is after the entity field, show all entities + if (context.pos >= afterField) { + const states = this._getStates(this.hass!.states); + + if (!states || !states.length) { + return null; + } + + // Find what's already typed after the field + const typedText = context.state.sliceDoc(afterField, context.pos); + + // Filter states based on what's typed + const filteredStates = typedText + ? states.filter((entityState) => + entityState.label + .toLowerCase() + .startsWith(typedText.toLowerCase()) + ) + : states; + + return { + from: afterField, + options: filteredStates, + validFor: /^[a-z_]*\.?\w*$/, + }; + } + } else if (listItemMatch) { + // Check if this is a list item under an entity_id field + const lineNumber = currentLine.number; + + // Look at previous lines to check if we're under an entity_id field + for (let i = lineNumber - 1; i > 0 && i >= lineNumber - 10; i--) { + const prevLine = context.state.doc.line(i); + const prevText = prevLine.text; + + // Stop if we hit a non-indented line (new field) + if ( + prevText.trim() && + !prevText.startsWith(" ") && + !prevText.startsWith("\t") + ) { + break; + } + + // Check if we found an entity property field + const entityListFieldRegex = new RegExp( + `^\\s*(${propertyPattern}):\\s*$` + ); + if (prevText.match(entityListFieldRegex)) { + // We're in a list under an entity field + const afterListMarker = currentLine.from + listItemMatch[0].length; + + if (context.pos >= afterListMarker) { + const states = this._getStates(this.hass!.states); + + if (!states || !states.length) { + return null; + } + + // Find what's already typed after the list marker + const typedText = context.state.sliceDoc( + afterListMarker, + context.pos + ); + + // Filter states based on what's typed + const filteredStates = typedText + ? states.filter((entityState) => + entityState.label + .toLowerCase() + .startsWith(typedText.toLowerCase()) + ) + : states; + + return { + from: afterListMarker, + options: filteredStates, + validFor: /^[a-z_]*\.?\w*$/, + }; + } + } + } + } + } + + // Original entity completion logic for non-YAML or when not in entity_id field const entityWord = context.matchBefore(/[a-z_]{3,}\.\w*/); if ( From 9afc4260c9657f4b2539cbc121a3b7b86703fff9 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 25 Jun 2025 01:27:09 -0400 Subject: [PATCH 110/216] Move AI task prefs to system -> general (#25904) Move AI task prefs --- src/data/ai_task.ts | 14 ++++---- .../dialog-automation-save.ts | 8 ++--- .../ai-task-pref.ts | 35 ++++++++++--------- .../config/core/ha-config-section-general.ts | 14 +++++++- .../ha-config-voice-assistants-assistants.ts | 7 ---- src/translations/en.json | 12 +++---- 6 files changed, 48 insertions(+), 42 deletions(-) rename src/panels/config/{voice-assistants => core}/ai-task-pref.ts (85%) diff --git a/src/data/ai_task.ts b/src/data/ai_task.ts index c4f42d0bc8..451fced583 100644 --- a/src/data/ai_task.ts +++ b/src/data/ai_task.ts @@ -1,12 +1,12 @@ import type { HomeAssistant } from "../types"; export interface AITaskPreferences { - gen_text_entity_id: string | null; + gen_data_entity_id: string | null; } -export interface GenTextTaskResult { +export interface GenDataTaskResult { conversation_id: string; - text: string; + data: string; } export const fetchAITaskPreferences = (hass: HomeAssistant) => @@ -23,17 +23,17 @@ export const saveAITaskPreferences = ( ...preferences, }); -export const generateTextAITask = async ( +export const generateDataAITask = async ( hass: HomeAssistant, task: { task_name: string; entity_id?: string; instructions: string; } -): Promise => { - const result = await hass.callService( +): Promise => { + const result = await hass.callService( "ai_task", - "generate_text", + "generate_data", task, undefined, true, diff --git a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts index 99fd37e5ff..2ba4069d0d 100644 --- a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts +++ b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts @@ -27,7 +27,7 @@ import type { import { supportsMarkdownHelper } from "../../../../common/translations/markdown_support"; import { fetchAITaskPreferences, - generateTextAITask, + generateDataAITask, } from "../../../../data/ai_task"; import { isComponentLoaded } from "../../../../common/config/is_component_loaded"; @@ -93,7 +93,7 @@ class DialogAutomationSave extends LitElement implements HassDialog { super.firstUpdated(changedProperties); if (isComponentLoaded(this.hass, "ai_task")) { fetchAITaskPreferences(this.hass).then((prefs) => { - this._canSuggest = prefs.gen_text_entity_id !== null; + this._canSuggest = prefs.gen_data_entity_id !== null; }); } } @@ -346,7 +346,7 @@ class DialogAutomationSave extends LitElement implements HassDialog { } private async _suggest() { - const result = await generateTextAITask(this.hass, { + const result = await generateDataAITask(this.hass, { task_name: "frontend:automation:save", instructions: `Suggest one name for the following Home Assistant automation. Your answer should only contain the name, without any additional text or formatting. @@ -356,7 +356,7 @@ The name should be short, descriptive, sentence case, and written in the languag ${dump(this._params.config)} `, }); - this._newName = result.text.trim(); + this._newName = result.data.trim(); } private async _save(): Promise { diff --git a/src/panels/config/voice-assistants/ai-task-pref.ts b/src/panels/config/core/ai-task-pref.ts similarity index 85% rename from src/panels/config/voice-assistants/ai-task-pref.ts rename to src/panels/config/core/ai-task-pref.ts index 9eba296781..2547f8a4ad 100644 --- a/src/panels/config/voice-assistants/ai-task-pref.ts +++ b/src/panels/config/core/ai-task-pref.ts @@ -47,9 +47,7 @@ export class AITaskPref extends LitElement { })} crossorigin="anonymous" referrerpolicy="no-referrer" - />${this.hass.localize( - "ui.panel.config.voice_assistants.ai_task.header" - )} + />${this.hass.localize("ui.panel.config.ai_task.header")}

    - ${this.hass!.localize( - "ui.panel.config.voice_assistants.ai_task.description", - { - button: html``, - } - )} + ${this.hass!.localize("ui.panel.config.ai_task.description", { + button: html``, + })}

    - ${this.hass!.localize( - "ui.panel.config.voice_assistants.ai_task.gen_text_header" - )} + ${this.hass!.localize("ui.panel.config.ai_task.gen_data_header")} ${this.hass!.localize( - "ui.panel.config.voice_assistants.ai_task.gen_text_description" + "ui.panel.config.ai_task.gen_data_description" )} @@ -119,6 +112,14 @@ export class AITaskPref extends LitElement { } static styles = css` + .card-header { + display: flex; + align-items: center; + } + .card-header img { + max-width: 28px; + margin-right: 16px; + } a { color: var(--primary-color); } diff --git a/src/panels/config/core/ha-config-section-general.ts b/src/panels/config/core/ha-config-section-general.ts index a3647c8ec1..7fc6aa15e4 100644 --- a/src/panels/config/core/ha-config-section-general.ts +++ b/src/panels/config/core/ha-config-section-general.ts @@ -25,8 +25,10 @@ import type { ConfigUpdateValues } from "../../../data/core"; import { saveCoreConfig } from "../../../data/core"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-subpage"; +import "./ai-task-pref"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant, ValueChangedEvent } from "../../../types"; +import { isComponentLoaded } from "../../../common/config/is_component_loaded"; @customElement("ha-config-section-general") class HaConfigSectionGeneral extends LitElement { @@ -265,6 +267,12 @@ class HaConfigSectionGeneral extends LitElement {
    + ${isComponentLoaded(this.hass, "ai_task") + ? html`` + : nothing}
    `; @@ -377,7 +385,8 @@ class HaConfigSectionGeneral extends LitElement { max-width: 1040px; margin: 0 auto; } - ha-card { + ha-card, + ai-task-pref { max-width: 600px; margin: 0 auto; height: 100%; @@ -385,6 +394,9 @@ class HaConfigSectionGeneral extends LitElement { flex-direction: column; display: flex; } + ha-card { + margin-bottom: 24px; + } .card-content { display: flex; justify-content: space-between; diff --git a/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts b/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts index f9aa48319b..ff959d7830 100644 --- a/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts +++ b/src/panels/config/voice-assistants/ha-config-voice-assistants-assistants.ts @@ -8,7 +8,6 @@ import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-tabs-subpage"; import type { HomeAssistant, Route } from "../../../types"; import "./assist-pref"; -import "./ai-task-pref"; import "./cloud-alexa-pref"; import "./cloud-discover"; import "./cloud-google-pref"; @@ -54,12 +53,6 @@ export class HaConfigVoiceAssistantsAssistants extends LitElement { > ` : nothing} - ${isComponentLoaded(this.hass, "ai_task") - ? html`` - : nothing} ${this.cloudStatus?.logged_in ? html` Date: Wed, 25 Jun 2025 12:50:21 +0200 Subject: [PATCH 111/216] Exclude entities in controls for areas dashboard (#25906) --- .../hui-area-controls-card-feature.ts | 15 ++++++++++----- src/panels/lovelace/card-features/types.ts | 1 + .../areas/areas-overview-view-strategy.ts | 10 +++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts index 2c75e2303c..353be677b9 100644 --- a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -67,18 +67,20 @@ export const supportsAreaControlsCardFeature = ( export const getAreaControlEntities = ( controls: AreaControl[], areaId: string, - hass: HomeAssistant + hass: HomeAssistant, + excludeEntities: string[] = [] ): Record => controls.reduce( (acc, control) => { const controlButton = AREA_CONTROLS_BUTTONS[control]; const filter = generateEntityFilter(hass, { area: areaId, + entity_category: "none", ...controlButton.filter, }); - acc[control] = Object.keys(hass.entities).filter((entityId) => - filter(entityId) + acc[control] = Object.keys(hass.entities).filter( + (entityId) => filter(entityId) && !excludeEntities.includes(entityId) ); return acc; }, @@ -134,7 +136,7 @@ class HuiAreaControlsCardFeature private _handleButtonTap(ev: MouseEvent) { ev.stopPropagation(); - if (!this.context?.area_id || !this.hass) { + if (!this.context?.area_id || !this.hass || !this._config) { return; } const control = (ev.currentTarget as any).control as AreaControl; @@ -142,6 +144,7 @@ class HuiAreaControlsCardFeature const controlEntities = this._controlEntities( this._controls, this.context.area_id, + this._config.exclude_entities, this.hass!.entities, this.hass!.devices, this.hass!.areas @@ -165,11 +168,12 @@ class HuiAreaControlsCardFeature ( controls: AreaControl[], areaId: string, + excludeEntities: string[] | undefined, // needed to update memoized function when entities, devices or areas change _entities: HomeAssistant["entities"], _devices: HomeAssistant["devices"], _areas: HomeAssistant["areas"] - ) => getAreaControlEntities(controls, areaId, this.hass!) + ) => getAreaControlEntities(controls, areaId, this.hass!, excludeEntities) ); protected render() { @@ -186,6 +190,7 @@ class HuiAreaControlsCardFeature const controlEntities = this._controlEntities( this._controls, this.context.area_id!, + this._config.exclude_entities, this.hass!.entities, this.hass!.devices, this.hass!.areas diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index bb0852955b..b486fa1c4d 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -165,6 +165,7 @@ export type AreaControl = (typeof AREA_CONTROLS)[number]; export interface AreaControlsCardFeatureConfig { type: "area-controls"; controls?: AreaControl[]; + exclude_entities?: string[]; } export type LovelaceCardFeatureConfig = diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 230b2230fe..4fdd45c075 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -71,11 +71,18 @@ export class AreasOverviewViewStrategy extends ReactiveElement { const areasCards = areasInFloors.map((area) => { const path = computeAreaPath(area.area_id); + const areaOptions = config.areas_options?.[area.area_id] || {}; + + const hiddenEntities = Object.values(areaOptions.groups_options || {}) + .map((display) => display.hidden || []) + .flat(); + const controls: AreaControl[] = ["light", "fan"]; const controlEntities = getAreaControlEntities( controls, area.area_id, - hass + hass, + hiddenEntities ); const filteredControls = controls.filter( @@ -101,6 +108,7 @@ export class AreasOverviewViewStrategy extends ReactiveElement { { type: "area-controls", controls: filteredControls, + exclude_entities: hiddenEntities, }, ] : [], From fcbc8de95a55ade37b50a834a2f35530976c6ebe Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Jun 2025 14:16:11 +0200 Subject: [PATCH 112/216] Update device row in integration page (#25907) * Update ha-config-entry-device-row.ts * Update src/panels/config/integrations/ha-config-entry-device-row.ts --------- Co-authored-by: Paulus Schoutsen --- .../ha-config-entry-device-row.ts | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/src/panels/config/integrations/ha-config-entry-device-row.ts b/src/panels/config/integrations/ha-config-entry-device-row.ts index d122f3c67b..9bf576fb33 100644 --- a/src/panels/config/integrations/ha-config-entry-device-row.ts +++ b/src/panels/config/integrations/ha-config-entry-device-row.ts @@ -1,7 +1,9 @@ import { mdiCogOutline, mdiDelete, + mdiDevices, mdiDotsVertical, + mdiPencil, mdiStopCircleOutline, } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; @@ -24,6 +26,7 @@ import { } from "../../lovelace/custom-card-helpers"; import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail"; import "./ha-config-sub-entry-row"; +import { stopPropagation } from "../../../common/dom/stop_propagation"; @customElement("ha-config-entry-device-row") class HaConfigEntryDeviceRow extends LitElement { @@ -49,34 +52,46 @@ class HaConfigEntryDeviceRow extends LitElement { area ? area.name : undefined, ].filter(Boolean); - return html` -
    ${computeDeviceNameDisplay(device, this.hass)}
    + return html` + +
    ${computeDeviceNameDisplay(device, this.hass)}
    ${supportingText.join(" • ")} ${supportingText.length && entities.length ? " • " : nothing} ${ entities.length - ? html`${this.hass.localize( + ? this.narrow + ? this.hass.localize( "ui.panel.config.integrations.config_entry.entities", { count: entities.length } - )}` + ) + : html`${this.hass.localize( + "ui.panel.config.integrations.config_entry.entities", + { count: entities.length } + )}` : nothing } - - + ${ + !this.narrow + ? html` + ` + : nothing + } -
    +
    ${ !this.narrow ? html` - + this.entities?.filter((entity) => entity.device_id === this.device.id); - private _handleConfigureDevice() { + private _handleConfigureDevice(ev: MouseEvent) { + ev.stopPropagation(); // Prevent triggering the click handler on the list item showDeviceRegistryDetailDialog(this, { device: this.device, updateEntry: async (updates) => { @@ -204,6 +220,9 @@ class HaConfigEntryDeviceRow extends LitElement { ha-md-list-item { --md-list-item-leading-space: 56px; } + :host([narrow]) ha-md-list-item { + --md-list-item-leading-space: 16px; + } .vertical-divider { height: 100%; width: 1px; From 884341656f1c4c3d8030479196287aee8da54257 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 25 Jun 2025 15:07:31 +0200 Subject: [PATCH 113/216] Add full-screen button to code editors (#25903) --- src/components/ha-code-editor.ts | 153 +++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index 190b456fbd..be825c2b42 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -6,6 +6,7 @@ import type { } from "@codemirror/autocomplete"; import type { Extension, TransactionSpec } from "@codemirror/state"; import type { EditorView, KeyBinding, ViewUpdate } from "@codemirror/view"; +import { mdiArrowExpand, mdiArrowCollapse } from "@mdi/js"; import type { HassEntities } from "home-assistant-js-websocket"; import type { PropertyValues } from "lit"; import { css, ReactiveElement } from "lit"; @@ -15,6 +16,7 @@ import { fireEvent } from "../common/dom/fire_event"; import { stopPropagation } from "../common/dom/stop_propagation"; import type { HomeAssistant } from "../types"; import "./ha-icon"; +import "./ha-icon-button"; declare global { interface HASSDomEvents { @@ -59,8 +61,13 @@ export class HaCodeEditor extends ReactiveElement { @property({ type: Boolean }) public error = false; + @property({ type: Boolean, attribute: "enable-fullscreen" }) + public enableFullscreen = true; + @state() private _value = ""; + @state() private _isFullscreen = false; + // eslint-disable-next-line @typescript-eslint/consistent-type-imports private _loadedCodeMirror?: typeof import("../resources/codemirror"); @@ -92,6 +99,7 @@ export class HaCodeEditor extends ReactiveElement { this.requestUpdate(); } this.addEventListener("keydown", stopPropagation); + this.addEventListener("keydown", this._handleKeyDown); // This is unreachable as editor will not exist yet, // but focus should not behave like this for good a11y. // (@steverep to fix in autofocus PR) @@ -106,6 +114,10 @@ export class HaCodeEditor extends ReactiveElement { public disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener("keydown", stopPropagation); + this.removeEventListener("keydown", this._handleKeyDown); + if (this._isFullscreen) { + this._toggleFullscreen(); + } this.updateComplete.then(() => { this.codemirror!.destroy(); delete this.codemirror; @@ -164,6 +176,12 @@ export class HaCodeEditor extends ReactiveElement { if (changedProps.has("error")) { this.classList.toggle("error-state", this.error); } + if (changedProps.has("_isFullscreen")) { + this.classList.toggle("fullscreen", this._isFullscreen); + } + if (changedProps.has("enableFullscreen")) { + this._updateFullscreenButton(); + } } private get _mode() { @@ -238,8 +256,74 @@ export class HaCodeEditor extends ReactiveElement { }), parent: this.renderRoot, }); + + this._updateFullscreenButton(); } + private _updateFullscreenButton() { + const existingButton = this.renderRoot.querySelector(".fullscreen-button"); + + if (!this.enableFullscreen) { + // Remove button if it exists and fullscreen is disabled + if (existingButton) { + existingButton.remove(); + } + // Exit fullscreen if currently in fullscreen mode + if (this._isFullscreen) { + this._isFullscreen = false; + } + return; + } + + // Create button if it doesn't exist + if (!existingButton) { + const button = document.createElement("ha-icon-button"); + (button as any).path = this._isFullscreen + ? mdiArrowCollapse + : mdiArrowExpand; + button.setAttribute( + "label", + this._isFullscreen ? "Exit fullscreen" : "Enter fullscreen" + ); + button.classList.add("fullscreen-button"); + // Use bound method to ensure proper this context + button.addEventListener("click", this._handleFullscreenClick); + this.renderRoot.appendChild(button); + } else { + // Update existing button + (existingButton as any).path = this._isFullscreen + ? mdiArrowCollapse + : mdiArrowExpand; + existingButton.setAttribute( + "label", + this._isFullscreen ? "Exit fullscreen" : "Enter fullscreen" + ); + } + } + + private _handleFullscreenClick = (e: Event) => { + e.preventDefault(); + e.stopPropagation(); + this._toggleFullscreen(); + }; + + private _toggleFullscreen() { + this._isFullscreen = !this._isFullscreen; + this._updateFullscreenButton(); + } + + private _handleKeyDown = (e: KeyboardEvent) => { + if (this._isFullscreen && e.key === "Escape") { + e.preventDefault(); + e.stopPropagation(); + this._toggleFullscreen(); + } else if (e.key === "F11" && this.enableFullscreen) { + e.preventDefault(); + e.stopPropagation(); + this._toggleFullscreen(); + } + }; + private _getStates = memoizeOne((states: HassEntities): Completion[] => { if (!states) { return []; @@ -460,9 +544,78 @@ export class HaCodeEditor extends ReactiveElement { }; static styles = css` + :host { + position: relative; + display: block; + } + :host(.error-state) .cm-gutters { border-color: var(--error-state-color, red); } + + .fullscreen-button { + position: absolute; + top: 8px; + right: 8px; + z-index: 10; + color: var(--secondary-text-color); + background-color: var(--card-background-color); + border-radius: 50%; + opacity: 0.6; + transition: opacity 0.2s; + --mdc-icon-button-size: 32px; + --mdc-icon-size: 18px; + /* Ensure button is clickable on iOS */ + cursor: pointer; + -webkit-tap-highlight-color: transparent; + touch-action: manipulation; + } + + .fullscreen-button:hover, + .fullscreen-button:active { + opacity: 1; + } + + @media (hover: none) { + .fullscreen-button { + opacity: 0.8; + } + } + + :host(.fullscreen) { + position: fixed !important; + top: var(--header-height, 56px) !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + z-index: 9999 !important; + background-color: var(--primary-background-color) !important; + margin: 0 !important; + padding: 16px !important; + /* Respect iOS safe areas while accounting for header */ + padding-top: max(16px, env(safe-area-inset-top)) !important; + padding-left: max(16px, env(safe-area-inset-left)) !important; + padding-right: max(16px, env(safe-area-inset-right)) !important; + padding-bottom: max(16px, env(safe-area-inset-bottom)) !important; + box-sizing: border-box !important; + display: flex !important; + flex-direction: column !important; + } + + :host(.fullscreen) .cm-editor { + height: 100% !important; + max-height: 100% !important; + border-radius: 0 !important; + } + + :host(.fullscreen) .fullscreen-button { + position: fixed; + top: calc( + var(--header-height, 56px) + max(8px, env(safe-area-inset-top)) + ); + right: max(24px, calc(env(safe-area-inset-right) + 8px)); + z-index: 10000; + } `; } From 5c1a8029bfb7e2bdd8511ff775bdb81caa2c5d92 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 25 Jun 2025 06:11:23 -0700 Subject: [PATCH 114/216] Fix entity selector slicing value on load (#25854) --- src/components/ha-selector/ha-selector-area.ts | 2 +- src/components/ha-selector/ha-selector-device.ts | 2 +- src/components/ha-selector/ha-selector-entity.ts | 2 +- src/components/ha-selector/ha-selector-floor.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ha-selector/ha-selector-area.ts b/src/components/ha-selector/ha-selector-area.ts index 1966214ab3..d2abf64449 100644 --- a/src/components/ha-selector/ha-selector-area.ts +++ b/src/components/ha-selector/ha-selector-area.ts @@ -54,7 +54,7 @@ export class HaAreaSelector extends LitElement { } protected willUpdate(changedProperties: PropertyValues): void { - if (changedProperties.has("selector") && this.value !== undefined) { + if (changedProperties.get("selector") && this.value !== undefined) { if (this.selector.area?.multiple && !Array.isArray(this.value)) { this.value = [this.value]; fireEvent(this, "value-changed", { value: this.value }); diff --git a/src/components/ha-selector/ha-selector-device.ts b/src/components/ha-selector/ha-selector-device.ts index 0ae3ca0fc6..8ce9b26860 100644 --- a/src/components/ha-selector/ha-selector-device.ts +++ b/src/components/ha-selector/ha-selector-device.ts @@ -56,7 +56,7 @@ export class HaDeviceSelector extends LitElement { } protected willUpdate(changedProperties: PropertyValues): void { - if (changedProperties.has("selector") && this.value !== undefined) { + if (changedProperties.get("selector") && this.value !== undefined) { if (this.selector.device?.multiple && !Array.isArray(this.value)) { this.value = [this.value]; fireEvent(this, "value-changed", { value: this.value }); diff --git a/src/components/ha-selector/ha-selector-entity.ts b/src/components/ha-selector/ha-selector-entity.ts index df3a7eab76..fb9a2eeae1 100644 --- a/src/components/ha-selector/ha-selector-entity.ts +++ b/src/components/ha-selector/ha-selector-entity.ts @@ -43,7 +43,7 @@ export class HaEntitySelector extends LitElement { } protected willUpdate(changedProperties: PropertyValues): void { - if (changedProperties.has("selector") && this.value !== undefined) { + if (changedProperties.get("selector") && this.value !== undefined) { if (this.selector.entity?.multiple && !Array.isArray(this.value)) { this.value = [this.value]; fireEvent(this, "value-changed", { value: this.value }); diff --git a/src/components/ha-selector/ha-selector-floor.ts b/src/components/ha-selector/ha-selector-floor.ts index f756a77426..f7135c2158 100644 --- a/src/components/ha-selector/ha-selector-floor.ts +++ b/src/components/ha-selector/ha-selector-floor.ts @@ -54,7 +54,7 @@ export class HaFloorSelector extends LitElement { } protected willUpdate(changedProperties: PropertyValues): void { - if (changedProperties.has("selector") && this.value !== undefined) { + if (changedProperties.get("selector") && this.value !== undefined) { if (this.selector.floor?.multiple && !Array.isArray(this.value)) { this.value = [this.value]; fireEvent(this, "value-changed", { value: this.value }); From 174d54396fba1baaac2f1321c88db81ef4ab3bdd Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 25 Jun 2025 15:14:41 +0200 Subject: [PATCH 115/216] Add cover controls to area card and improve areas dashboard (#25892) --- src/common/entity/group_entities.ts | 68 +++++++++ src/common/entity/state_color.ts | 22 ++- src/components/ha-control-button-group.ts | 1 + src/components/ha-domain-icon.ts | 19 ++- src/data/icons.ts | 15 +- .../common/card-feature-styles.ts | 3 + .../hui-area-controls-card-feature.ts | 142 ++++++++++++------ .../card-features/hui-card-feature.ts | 5 + .../card-features/hui-card-features.ts | 5 + src/panels/lovelace/card-features/types.ts | 18 ++- src/panels/lovelace/cards/hui-area-card.ts | 1 + src/panels/lovelace/cards/types.ts | 9 +- .../hui-area-controls-card-feature-editor.ts | 17 ++- .../areas/areas-overview-view-strategy.ts | 11 +- src/panels/lovelace/types.ts | 2 + src/translations/en.json | 69 ++++++++- 16 files changed, 336 insertions(+), 71 deletions(-) create mode 100644 src/common/entity/group_entities.ts diff --git a/src/common/entity/group_entities.ts b/src/common/entity/group_entities.ts new file mode 100644 index 0000000000..32f9634af5 --- /dev/null +++ b/src/common/entity/group_entities.ts @@ -0,0 +1,68 @@ +import { callService, type HassEntity } from "home-assistant-js-websocket"; +import { computeStateDomain } from "./compute_state_domain"; +import { isUnavailableState, UNAVAILABLE } from "../../data/entity"; +import type { HomeAssistant } from "../../types"; + +export const computeGroupEntitiesState = (states: HassEntity[]): string => { + if (!states.length) { + return UNAVAILABLE; + } + + const validState = states.filter((stateObj) => isUnavailableState(stateObj)); + + if (!validState) { + return UNAVAILABLE; + } + + // Use the first state to determine the domain + // This assumes all states in the group have the same domain + const domain = computeStateDomain(states[0]); + + if (domain === "cover") { + for (const s of ["opening", "closing", "open"]) { + if (states.some((stateObj) => stateObj.state === s)) { + return s; + } + } + return "closed"; + } + + if (states.some((stateObj) => stateObj.state === "on")) { + return "on"; + } + return "off"; +}; + +export const toggleGroupEntities = ( + hass: HomeAssistant, + states: HassEntity[] +) => { + if (!states.length) { + return; + } + + // Use the first state to determine the domain + // This assumes all states in the group have the same domain + const domain = computeStateDomain(states[0]); + + const state = computeGroupEntitiesState(states); + + const isOn = state === "on" || state === "open"; + + let service = isOn ? "turn_off" : "turn_on"; + if (domain === "cover") { + if (state === "opening" || state === "closing") { + // If the cover is opening or closing, we toggle it to stop it + service = "stop_cover"; + } else { + // For covers, we use the open/close service + service = isOn ? "close_cover" : "open_cover"; + } + } + + const entitiesIds = states.map((stateObj) => stateObj.entity_id); + + callService(hass.connection, domain, service, { + entity_id: entitiesIds, + }); +}; diff --git a/src/common/entity/state_color.ts b/src/common/entity/state_color.ts index 474fee0e59..976f56ab11 100644 --- a/src/common/entity/state_color.ts +++ b/src/common/entity/state_color.ts @@ -64,15 +64,27 @@ export const domainStateColorProperties = ( const compareState = state !== undefined ? state : stateObj.state; const active = stateActive(stateObj, state); + return domainColorProperties( + domain, + stateObj.attributes.device_class, + compareState, + active + ); +}; + +export const domainColorProperties = ( + domain: string, + deviceClass: string | undefined, + state: string, + active: boolean +) => { const properties: string[] = []; - const stateKey = slugify(compareState, "_"); + const stateKey = slugify(state, "_"); const activeKey = active ? "active" : "inactive"; - const dc = stateObj.attributes.device_class; - - if (dc) { - properties.push(`--state-${domain}-${dc}-${stateKey}-color`); + if (deviceClass) { + properties.push(`--state-${domain}-${deviceClass}-${stateKey}-color`); } properties.push( diff --git a/src/components/ha-control-button-group.ts b/src/components/ha-control-button-group.ts index 5c59d0801f..fa9d0717c1 100644 --- a/src/components/ha-control-button-group.ts +++ b/src/components/ha-control-button-group.ts @@ -26,6 +26,7 @@ export class HaControlButtonGroup extends LitElement { .container { display: flex; flex-direction: row; + justify-content: var(--control-button-group-alignment, start); width: 100%; height: 100%; } diff --git a/src/components/ha-domain-icon.ts b/src/components/ha-domain-icon.ts index 6994c32c99..7d30d2f4a9 100644 --- a/src/components/ha-domain-icon.ts +++ b/src/components/ha-domain-icon.ts @@ -18,6 +18,8 @@ export class HaDomainIcon extends LitElement { @property({ attribute: false }) public deviceClass?: string; + @property({ attribute: false }) public state?: string; + @property() public icon?: string; @property({ attribute: "brand-fallback", type: Boolean }) @@ -36,14 +38,17 @@ export class HaDomainIcon extends LitElement { return this._renderFallback(); } - const icon = domainIcon(this.hass, this.domain, this.deviceClass).then( - (icn) => { - if (icn) { - return html``; - } - return this._renderFallback(); + const icon = domainIcon( + this.hass, + this.domain, + this.deviceClass, + this.state + ).then((icn) => { + if (icn) { + return html``; } - ); + return this._renderFallback(); + }); return html`${until(icon)}`; } diff --git a/src/data/icons.ts b/src/data/icons.ts index 5d870aac2e..4c6699c038 100644 --- a/src/data/icons.ts +++ b/src/data/icons.ts @@ -504,14 +504,25 @@ export const serviceSectionIcon = async ( export const domainIcon = async ( hass: HomeAssistant, domain: string, - deviceClass?: string + deviceClass?: string, + state?: string ): Promise => { const entityComponentIcons = await getComponentIcons(hass, domain); if (entityComponentIcons) { const translations = (deviceClass && entityComponentIcons[deviceClass]) || entityComponentIcons._; - return translations?.default; + // First check for exact state match + if (state && translations.state?.[state]) { + return translations.state[state]; + } + // Then check for range-based icons if we have a numeric state + if (state !== undefined && translations.range && !isNaN(Number(state))) { + return getIconFromRange(Number(state), translations.range); + } + // Fallback to default icon + return translations.default; } + return undefined; }; diff --git a/src/panels/lovelace/card-features/common/card-feature-styles.ts b/src/panels/lovelace/card-features/common/card-feature-styles.ts index 799570cc99..15a6cebcd1 100644 --- a/src/panels/lovelace/card-features/common/card-feature-styles.ts +++ b/src/panels/lovelace/card-features/common/card-feature-styles.ts @@ -25,6 +25,9 @@ export const cardFeatureStyles = css` flex-basis: 20px; --control-button-padding: 0px; } + ha-control-button-group[no-stretch] > ha-control-button { + max-width: 48px; + } ha-control-button { --control-button-focus-color: var(--feature-color); } diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts index 353be677b9..ceb789d02c 100644 --- a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -1,17 +1,22 @@ -import { mdiFan, mdiLightbulb, mdiToggleSwitch } from "@mdi/js"; -import { callService, type HassEntity } from "home-assistant-js-websocket"; -import { LitElement, css, html, nothing } from "lit"; +import type { HassEntity } from "home-assistant-js-websocket"; +import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; +import { ensureArray } from "../../../common/array/ensure-array"; +import { generateEntityFilter } from "../../../common/entity/entity_filter"; import { - generateEntityFilter, - type EntityFilter, -} from "../../../common/entity/entity_filter"; + computeGroupEntitiesState, + toggleGroupEntities, +} from "../../../common/entity/group_entities"; import { stateActive } from "../../../common/entity/state_active"; +import { domainColorProperties } from "../../../common/entity/state_color"; import "../../../components/ha-control-button"; import "../../../components/ha-control-button-group"; import "../../../components/ha-svg-icon"; import type { AreaRegistryEntry } from "../../../data/area_registry"; +import { forwardHaptic } from "../../../data/haptics"; +import { computeCssVariable } from "../../../resources/css-variables"; import type { HomeAssistant } from "../../../types"; import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types"; import { cardFeatureStyles } from "./common/card-feature-styles"; @@ -19,41 +24,55 @@ import type { AreaControl, AreaControlsCardFeatureConfig, LovelaceCardFeatureContext, + LovelaceCardFeaturePosition, } from "./types"; import { AREA_CONTROLS } from "./types"; interface AreaControlsButton { - iconPath: string; - onService: string; - offService: string; - filter: EntityFilter; + offIcon?: string; + onIcon?: string; + filter: { + domain: string; + device_class?: string; + }; } +const coverButton = (deviceClass: string) => ({ + filter: { + domain: "cover", + device_class: deviceClass, + }, +}); + export const AREA_CONTROLS_BUTTONS: Record = { light: { - iconPath: mdiLightbulb, + // Overrides the icons for lights + offIcon: "mdi:lightbulb-off", + onIcon: "mdi:lightbulb", filter: { domain: "light", }, - onService: "light.turn_on", - offService: "light.turn_off", }, fan: { - iconPath: mdiFan, filter: { domain: "fan", }, - onService: "fan.turn_on", - offService: "fan.turn_off", }, switch: { - iconPath: mdiToggleSwitch, filter: { domain: "switch", }, - onService: "switch.turn_on", - offService: "switch.turn_off", }, + "cover-blind": coverButton("blind"), + "cover-curtain": coverButton("curtain"), + "cover-damper": coverButton("damper"), + "cover-awning": coverButton("awning"), + "cover-door": coverButton("door"), + "cover-garage": coverButton("garage"), + "cover-gate": coverButton("gate"), + "cover-shade": coverButton("shade"), + "cover-shutter": coverButton("shutter"), + "cover-window": coverButton("window"), }; export const supportsAreaControlsCardFeature = ( @@ -87,6 +106,8 @@ export const getAreaControlEntities = ( {} as Record ); +export const MAX_DEFAULT_AREA_CONTROLS = 4; + @customElement("hui-area-controls-card-feature") class HuiAreaControlsCardFeature extends LitElement @@ -96,6 +117,9 @@ class HuiAreaControlsCardFeature @property({ attribute: false }) public context?: LovelaceCardFeatureContext; + @property({ attribute: false }) + public position?: LovelaceCardFeaturePosition; + @state() private _config?: AreaControlsCardFeatureConfig; private get _area() { @@ -151,17 +175,12 @@ class HuiAreaControlsCardFeature ); const entitiesIds = controlEntities[control]; - const { onService, offService } = AREA_CONTROLS_BUTTONS[control]; + const entities = entitiesIds + .map((entityId) => this.hass!.states[entityId] as HassEntity | undefined) + .filter((v): v is HassEntity => Boolean(v)); - const isOn = entitiesIds.some((entityId) => - stateActive(this.hass!.states[entityId] as HassEntity) - ); - - const [domain, service] = (isOn ? offService : onService).split("."); - - callService(this.hass!.connection, domain, service, { - entity_id: entitiesIds, - }); + forwardHaptic("light"); + toggleGroupEntities(this.hass, entities); } private _controlEntities = memoizeOne( @@ -200,33 +219,67 @@ class HuiAreaControlsCardFeature (control) => controlEntities[control].length > 0 ); - if (!supportedControls.length) { + const displayControls = this._config.controls + ? supportedControls + : supportedControls.slice(0, MAX_DEFAULT_AREA_CONTROLS); // Limit to max if using default controls + + if (!displayControls.length) { return nothing; } return html` - - ${supportedControls.map((control) => { + + ${displayControls.map((control) => { const button = AREA_CONTROLS_BUTTONS[control]; - const entities = controlEntities[control]; - const active = entities.some((entityId) => { - const stateObj = this.hass!.states[entityId] as - | HassEntity - | undefined; - if (!stateObj) { - return false; - } - return stateActive(stateObj); - }); + const entityIds = controlEntities[control]; + + const entities = entityIds + .map( + (entityId) => + this.hass!.states[entityId] as HassEntity | undefined + ) + .filter((v): v is HassEntity => Boolean(v)); + + const groupState = computeGroupEntitiesState(entities); + + const active = entities[0] + ? stateActive(entities[0], groupState) + : false; + + const label = this.hass!.localize( + `ui.card_features.area_controls.${control}.${active ? "off" : "on"}` + ); + + const icon = active ? button.onIcon : button.offIcon; + + const domain = button.filter.domain; + const deviceClass = button.filter.device_class + ? ensureArray(button.filter.device_class)[0] + : undefined; + + const activeColor = computeCssVariable( + domainColorProperties(domain, deviceClass, groupState, true) + ); return html` - + `; })} @@ -238,6 +291,9 @@ class HuiAreaControlsCardFeature return [ cardFeatureStyles, css` + ha-control-button-group { + --control-button-group-alignment: flex-end; + } ha-control-button { --active-color: var(--state-active-color); --control-button-focus-color: var(--state-active-color); diff --git a/src/panels/lovelace/card-features/hui-card-feature.ts b/src/panels/lovelace/card-features/hui-card-feature.ts index 12592b4fba..fa92cc89ae 100644 --- a/src/panels/lovelace/card-features/hui-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-card-feature.ts @@ -7,6 +7,7 @@ import type { LovelaceCardFeature } from "../types"; import type { LovelaceCardFeatureConfig, LovelaceCardFeatureContext, + LovelaceCardFeaturePosition, } from "./types"; @customElement("hui-card-feature") @@ -19,6 +20,9 @@ export class HuiCardFeature extends LitElement { @property({ attribute: false }) public color?: string; + @property({ attribute: false }) + public position?: LovelaceCardFeaturePosition; + private _element?: LovelaceCardFeature | HuiErrorCard; private _getFeatureElement(feature: LovelaceCardFeatureConfig) { @@ -41,6 +45,7 @@ export class HuiCardFeature extends LitElement { element.hass = this.hass; element.context = this.context; element.color = this.color; + element.position = this.position; // Backwards compatibility from custom card features if (this.context.entity_id) { const stateObj = this.hass.states[this.context.entity_id]; diff --git a/src/panels/lovelace/card-features/hui-card-features.ts b/src/panels/lovelace/card-features/hui-card-features.ts index b723c3f2a0..138e639657 100644 --- a/src/panels/lovelace/card-features/hui-card-features.ts +++ b/src/panels/lovelace/card-features/hui-card-features.ts @@ -5,6 +5,7 @@ import "./hui-card-feature"; import type { LovelaceCardFeatureConfig, LovelaceCardFeatureContext, + LovelaceCardFeaturePosition, } from "./types"; @customElement("hui-card-features") @@ -17,6 +18,9 @@ export class HuiCardFeatures extends LitElement { @property({ attribute: false }) public color?: string; + @property({ attribute: false }) + public position?: LovelaceCardFeaturePosition; + protected render() { if (!this.features) { return nothing; @@ -29,6 +33,7 @@ export class HuiCardFeatures extends LitElement { .context=${this.context} .color=${this.color} .feature=${feature} + .position=${this.position} > ` )} diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index b486fa1c4d..4b74481431 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -158,7 +158,21 @@ export interface UpdateActionsCardFeatureConfig { backup?: "yes" | "no" | "ask"; } -export const AREA_CONTROLS = ["light", "fan", "switch"] as const; +export const AREA_CONTROLS = [ + "light", + "fan", + "cover-shutter", + "cover-blind", + "cover-curtain", + "cover-shade", + "cover-awning", + "cover-garage", + "cover-gate", + "cover-door", + "cover-window", + "cover-damper", + "switch", +] as const; export type AreaControl = (typeof AREA_CONTROLS)[number]; @@ -168,6 +182,8 @@ export interface AreaControlsCardFeatureConfig { exclude_entities?: string[]; } +export type LovelaceCardFeaturePosition = "bottom" | "inline"; + export type LovelaceCardFeatureConfig = | AlarmModesCardFeatureConfig | ClimateFanModesCardFeatureConfig diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index 3f87f13f65..dc2844e731 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -514,6 +514,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { .context=${this._featureContext} .color=${this._config.color} .features=${features} + .position=${featurePosition} > ` : nothing} diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 5e04bfb1f0..38c58a6bed 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -9,7 +9,10 @@ import type { ThemeMode, TranslationDict, } from "../../../types"; -import type { LovelaceCardFeatureConfig } from "../card-features/types"; +import type { + LovelaceCardFeatureConfig, + LovelaceCardFeaturePosition, +} from "../card-features/types"; import type { LegacyStateFilter } from "../common/evaluate-filter"; import type { Condition, LegacyCondition } from "../common/validate-condition"; import type { HuiImage } from "../components/hui-image"; @@ -113,7 +116,7 @@ export interface AreaCardConfig extends LovelaceCardConfig { sensor_classes?: string[]; alert_classes?: string[]; features?: LovelaceCardFeatureConfig[]; - features_position?: "bottom" | "inline"; + features_position?: LovelaceCardFeaturePosition; } export interface ButtonCardConfig extends LovelaceCardConfig { @@ -564,7 +567,7 @@ export interface TileCardConfig extends LovelaceCardConfig { icon_hold_action?: ActionConfig; icon_double_tap_action?: ActionConfig; features?: LovelaceCardFeatureConfig[]; - features_position?: "bottom" | "inline"; + features_position?: LovelaceCardFeaturePosition; } export interface HeadingCardConfig extends LovelaceCardConfig { diff --git a/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts index ac79793497..819ce72dfb 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts @@ -9,7 +9,10 @@ import type { SchemaUnion, } from "../../../../components/ha-form/types"; import type { HomeAssistant } from "../../../../types"; -import { getAreaControlEntities } from "../../card-features/hui-area-controls-card-feature"; +import { + getAreaControlEntities, + MAX_DEFAULT_AREA_CONTROLS, +} from "../../card-features/hui-area-controls-card-feature"; import { AREA_CONTROLS, type AreaControl, @@ -72,7 +75,7 @@ export class HuiAreaControlsCardFeatureEditor ] as const satisfies readonly HaFormSchema[] ); - private _compatibleControls = memoizeOne( + private _supportedControls = memoizeOne( ( areaId: string, // needed to update memoized function when entities, devices or areas change @@ -99,14 +102,14 @@ export class HuiAreaControlsCardFeatureEditor return nothing; } - const compatibleControls = this._compatibleControls( + const supportedControls = this._supportedControls( this.context.area_id, this.hass.entities, this.hass.devices, this.hass.areas ); - if (compatibleControls.length === 0) { + if (supportedControls.length === 0) { return html` ${this.hass.localize( @@ -124,7 +127,7 @@ export class HuiAreaControlsCardFeatureEditor const schema = this._schema( this.hass.localize, data.customize_controls, - compatibleControls + supportedControls ); return html` @@ -143,12 +146,12 @@ export class HuiAreaControlsCardFeatureEditor .value as AreaControlsCardFeatureData; if (customize_controls && !config.controls) { - config.controls = this._compatibleControls( + config.controls = this._supportedControls( this.context!.area_id!, this.hass!.entities, this.hass!.devices, this.hass!.areas - ).concat(); + ).slice(0, MAX_DEFAULT_AREA_CONTROLS); // Limit to max default controls } if (!customize_controls && config.controls) { diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 4fdd45c075..3ff37a0b7c 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -6,7 +6,7 @@ import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/sec import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; import type { HomeAssistant } from "../../../../types"; import { getAreaControlEntities } from "../../card-features/hui-area-controls-card-feature"; -import type { AreaControl } from "../../card-features/types"; +import { AREA_CONTROLS, type AreaControl } from "../../card-features/types"; import type { AreaCardConfig, HeadingCardConfig } from "../../cards/types"; import type { EntitiesDisplay } from "./area-view-strategy"; import { computeAreaPath, getAreas } from "./helpers/areas-strategy-helper"; @@ -77,7 +77,9 @@ export class AreasOverviewViewStrategy extends ReactiveElement { .map((display) => display.hidden || []) .flat(); - const controls: AreaControl[] = ["light", "fan"]; + const controls: AreaControl[] = AREA_CONTROLS.filter( + (a) => a !== "switch" // Exclude switches control for areas as we don't know what the switches control + ); const controlEntities = getAreaControlEntities( controls, area.area_id, @@ -112,6 +114,11 @@ export class AreasOverviewViewStrategy extends ReactiveElement { }, ] : [], + grid_options: { + rows: 1, + columns: 12, + }, + features_position: "inline", navigation_path: path, }; }); diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index 40534349bf..ea580f59d8 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -13,6 +13,7 @@ import type { Constructor, HomeAssistant } from "../../types"; import type { LovelaceCardFeatureConfig, LovelaceCardFeatureContext, + LovelaceCardFeaturePosition, } from "./card-features/types"; import type { LovelaceElement, LovelaceElementConfig } from "./elements/types"; import type { LovelaceRow, LovelaceRowConfig } from "./entity-rows/types"; @@ -179,6 +180,7 @@ export interface LovelaceCardFeature extends HTMLElement { context?: LovelaceCardFeatureContext; setConfig(config: LovelaceCardFeatureConfig); color?: string; + position?: LovelaceCardFeaturePosition; } export interface LovelaceCardFeatureConstructor diff --git a/src/translations/en.json b/src/translations/en.json index 5bb35b2aab..ae3efff279 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -325,6 +325,62 @@ "low": "Low" } }, + "card_features": { + "area_controls": { + "light": { + "on": "Turn on area lights", + "off": "Turn off area lights" + }, + "fan": { + "on": "Turn on area fans", + "off": "Turn off area fans" + }, + "switch": { + "on": "Turn on area switches", + "off": "Turn off area switches" + }, + "cover-awning": { + "on": "Open area awnings", + "off": "Close area awnings" + }, + "cover-blind": { + "on": "Open area blinds", + "off": "Close area blinds" + }, + "cover-curtain": { + "on": "Open area curtains", + "off": "Close area curtains" + }, + "cover-damper": { + "on": "Open area dampers", + "off": "Close area dampers" + }, + "cover-door": { + "on": "Open area doors", + "off": "Close area doors" + }, + "cover-garage": { + "on": "Open garage door", + "off": "Close garage door" + }, + "cover-gate": { + "on": "Open area gates", + "off": "Close area gates" + }, + "cover-shade": { + "on": "Open area shades", + "off": "Close area shades" + }, + "cover-shutter": { + "on": "Open area shutters", + "off": "Close area shutters" + }, + "cover-window": { + "on": "Open area windows", + "off": "Close area windows" + } + } + }, "common": { "and": "and", "continue": "Continue", @@ -383,6 +439,7 @@ "markdown": "Markdown", "suggest_ai": "Suggest with AI" }, + "components": { "selectors": { "media": { @@ -7857,7 +7914,17 @@ "controls_options": { "light": "Lights", "fan": "Fans", - "switch": "Switches" + "switch": "Switches", + "cover-awning": "Awnings", + "cover-blind": "Blinds", + "cover-curtain": "Curtains", + "cover-damper": "Dampers", + "cover-door": "Doors", + "cover-garage": "Garage doors", + "cover-gate": "Gates", + "cover-shade": "Shades", + "cover-shutter": "Shutters", + "cover-window": "Windows" }, "no_compatible_controls": "No compatible controls available for this area" } From 2dfe5f50a6cedf7ac7f0b52828f7edff1d5e7cdc Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 25 Jun 2025 06:20:53 -0700 Subject: [PATCH 116/216] Support templates in action target (#25656) --- src/components/ha-service-control.ts | 43 ++++++++++++++++--- src/data/script.ts | 16 ++++++- .../types/ha-automation-action-service.ts | 2 +- .../action/developer-tools-action.ts | 2 +- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts index a31382ac2f..898959277d 100644 --- a/src/components/ha-service-control.ts +++ b/src/components/ha-service-control.ts @@ -276,6 +276,16 @@ export class HaServiceControl extends LitElement { private _getTargetedEntities = memoizeOne((target, value) => { const targetSelector = target ? { target } : { target: {} }; + if ( + hasTemplate(value?.target) || + hasTemplate(value?.data?.entity_id) || + hasTemplate(value?.data?.device_id) || + hasTemplate(value?.data?.area_id) || + hasTemplate(value?.data?.floor_id) || + hasTemplate(value?.data?.label_id) + ) { + return null; + } const targetEntities = ensureArray( value?.target?.entity_id || value?.data?.entity_id @@ -349,8 +359,11 @@ export class HaServiceControl extends LitElement { private _filterField( filter: ExtHassService["fields"][number]["filter"], - targetEntities: string[] + targetEntities: string[] | null ) { + if (targetEntities === null) { + return true; // Target is a template, show all fields + } if (!targetEntities.length) { return false; } @@ -386,8 +399,21 @@ export class HaServiceControl extends LitElement { } private _targetSelector = memoizeOne( - (targetSelector: TargetSelector | null | undefined) => - targetSelector ? { target: { ...targetSelector } } : { target: {} } + (targetSelector: TargetSelector | null | undefined, value) => { + if (!value || (typeof value === "object" && !Object.keys(value).length)) { + delete this._stickySelector.target; + } else if (hasTemplate(value)) { + if (typeof value === "string") { + this._stickySelector.target = { template: null }; + } else { + this._stickySelector.target = { object: null }; + } + } + return ( + this._stickySelector.target ?? + (targetSelector ? { target: { ...targetSelector } } : { target: {} }) + ); + } ); protected render() { @@ -482,7 +508,8 @@ export class HaServiceControl extends LitElement { > @@ -588,7 +615,7 @@ export class HaServiceControl extends LitElement { hasOptional: boolean, domain: string | undefined, serviceName: string | undefined, - targetEntities: string[] + targetEntities: string[] | null ) => { if ( dataField.filter && @@ -822,6 +849,10 @@ export class HaServiceControl extends LitElement { private _targetChanged(ev: CustomEvent) { ev.stopPropagation(); + if (ev.detail.isValid === false) { + // Don't clear an object selector that returns invalid YAML + return; + } const newValue = ev.detail.value; if (this._value?.target === newValue) { return; diff --git a/src/data/script.ts b/src/data/script.ts index 6128014dfc..c7ba8d6c79 100644 --- a/src/data/script.ts +++ b/src/data/script.ts @@ -14,6 +14,7 @@ import { literal, is, boolean, + refine, } from "superstruct"; import { arrayLiteralIncludes } from "../common/array/literal-includes"; import { navigate } from "../common/navigate"; @@ -49,13 +50,18 @@ export const targetStruct = object({ label_id: optional(union([string(), array(string())])), }); -export const serviceActionStruct: Describe = assign( +export const serviceActionStruct: Describe = assign( baseActionStruct, object({ action: optional(string()), service_template: optional(string()), entity_id: optional(string()), - target: optional(targetStruct), + target: optional( + union([ + targetStruct, + refine(string(), "has_template", (val) => hasTemplate(val)), + ]) + ), data: optional(object()), response_variable: optional(string()), metadata: optional(object()), @@ -132,6 +138,12 @@ export interface ServiceAction extends BaseAction { metadata?: Record; } +type ServiceActionWithTemplate = ServiceAction & { + target?: HassServiceTarget | string; +}; + +export type { ServiceActionWithTemplate }; + export interface DeviceAction extends BaseAction { type: string; device_id: string; diff --git a/src/panels/config/automation/action/types/ha-automation-action-service.ts b/src/panels/config/automation/action/types/ha-automation-action-service.ts index fa431c2f50..20f2da1c5a 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-service.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-service.ts @@ -42,7 +42,7 @@ export class HaServiceAction extends LitElement implements ActionElement { if ( this.action && Object.entries(this.action).some( - ([key, val]) => key !== "data" && hasTemplate(val) + ([key, val]) => !["data", "target"].includes(key) && hasTemplate(val) ) ) { fireEvent( diff --git a/src/panels/developer-tools/action/developer-tools-action.ts b/src/panels/developer-tools/action/developer-tools-action.ts index d964555e27..d28f89c714 100644 --- a/src/panels/developer-tools/action/developer-tools-action.ts +++ b/src/panels/developer-tools/action/developer-tools-action.ts @@ -535,7 +535,7 @@ class HaPanelDevAction extends LitElement { if ( this._serviceData && Object.entries(this._serviceData).some( - ([key, val]) => key !== "data" && hasTemplate(val) + ([key, val]) => !["data", "target"].includes(key) && hasTemplate(val) ) ) { this._yamlMode = true; From e7a04eb3d22b09a69f439858960638532c931139 Mon Sep 17 00:00:00 2001 From: Leon <29leon42005+github@gmail.com> Date: Wed, 25 Jun 2025 15:22:21 +0200 Subject: [PATCH 117/216] Short-format negative and small numbers in energy-distribution-card (#25862) --- src/data/energy.ts | 11 ++++++++--- test/data/energy.test.ts | 39 +++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/data/energy.ts b/src/data/energy.ts index 0ee60fde90..3510c92125 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -1114,12 +1114,16 @@ export const formatConsumptionShort = ( if (!consumption) { return `0 ${unit}`; } - const units = ["kWh", "MWh", "GWh", "TWh"]; + const units = ["Wh", "kWh", "MWh", "GWh", "TWh"]; let pickedUnit = unit; let val = consumption; let unitIndex = units.findIndex((u) => u === unit); if (unitIndex >= 0) { - while (val >= 1000 && unitIndex < units.length - 1) { + while (Math.abs(val) < 1 && unitIndex > 0) { + val *= 1000; + unitIndex--; + } + while (Math.abs(val) >= 1000 && unitIndex < units.length - 1) { val /= 1000; unitIndex++; } @@ -1127,7 +1131,8 @@ export const formatConsumptionShort = ( } return ( formatNumber(val, hass.locale, { - maximumFractionDigits: val < 10 ? 2 : val < 100 ? 1 : 0, + maximumFractionDigits: + Math.abs(val) < 10 ? 2 : Math.abs(val) < 100 ? 1 : 0, }) + " " + pickedUnit diff --git a/test/data/energy.test.ts b/test/data/energy.test.ts index d70ad81593..02d8fe170e 100644 --- a/test/data/energy.test.ts +++ b/test/data/energy.test.ts @@ -68,15 +68,18 @@ describe("Energy Short Format Test", () => { }; const hass = { locale: defaultLocale } as HomeAssistant; - it("Formats", () => { + it("No Unit conversion", () => { + assert.strictEqual(formatConsumptionShort(hass, 0, "Wh"), "0 Wh"); assert.strictEqual(formatConsumptionShort(hass, 0, "kWh"), "0 kWh"); assert.strictEqual(formatConsumptionShort(hass, 0, "GWh"), "0 GWh"); assert.strictEqual(formatConsumptionShort(hass, 0, "gal"), "0 gal"); assert.strictEqual( - formatConsumptionShort(hass, 0.12345, "kWh"), - "0.12 kWh" + formatConsumptionShort(hass, 10000.12345, "gal"), + "10,000 gal" ); + + assert.strictEqual(formatConsumptionShort(hass, 1.2345, "kWh"), "1.23 kWh"); assert.strictEqual( formatConsumptionShort(hass, 10.12345, "kWh"), "10.1 kWh" @@ -85,6 +88,10 @@ describe("Energy Short Format Test", () => { formatConsumptionShort(hass, 500.12345, "kWh"), "500 kWh" ); + + assert.strictEqual(formatConsumptionShort(hass, 10.01, "kWh"), "10 kWh"); + }); + it("Upward Unit conversion", () => { assert.strictEqual( formatConsumptionShort(hass, 1512.34567, "kWh"), "1.51 MWh" @@ -105,23 +112,31 @@ describe("Energy Short Format Test", () => { formatConsumptionShort(hass, 15123456789.9, "kWh"), "15.1 TWh" ); - assert.strictEqual( formatConsumptionShort(hass, 15123456789000.9, "kWh"), "15,123 TWh" ); - - assert.strictEqual(formatConsumptionShort(hass, 1000.1, "GWh"), "1 TWh"); - + }); + it("Downward Unit conversion", () => { + assert.strictEqual(formatConsumptionShort(hass, 0.00012, "kWh"), "0.12 Wh"); + assert.strictEqual(formatConsumptionShort(hass, 0.12345, "kWh"), "123 Wh"); assert.strictEqual( - formatConsumptionShort(hass, 10000.12345, "gal"), - "10,000 gal" + formatConsumptionShort(hass, 0.00001234, "TWh"), + "12.3 MWh" + ); + }); + it("Negativ Consumption", () => { + assert.strictEqual( + formatConsumptionShort(hass, -500.123, "kWh"), + "-500 kWh" ); - - // Don't really modify negative numbers, but make sure it's something sane. assert.strictEqual( formatConsumptionShort(hass, -1234.56, "kWh"), - "-1,234.56 kWh" + "-1.23 MWh" + ); + assert.strictEqual( + formatConsumptionShort(hass, -0.001234, "kWh"), + "-1.23 Wh" ); }); }); From 3ab6a02994a63f11a67d153a4f3cce0b5d5d091a Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 25 Jun 2025 06:35:14 -0700 Subject: [PATCH 118/216] Improve settings page accessibility (#25885) --- src/components/ha-navigation-list.ts | 2 +- src/panels/config/dashboard/ha-config-updates.ts | 2 +- src/panels/config/repairs/ha-config-repairs.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ha-navigation-list.ts b/src/components/ha-navigation-list.ts index a0bc4ef64e..f309dd1a4e 100644 --- a/src/components/ha-navigation-list.ts +++ b/src/components/ha-navigation-list.ts @@ -44,7 +44,7 @@ class HaNavigationList extends LitElement { >
    - ${page.name} + ${page.name} ${this.hasSecondary ? html`${page.description}` : ""} diff --git a/src/panels/config/dashboard/ha-config-updates.ts b/src/panels/config/dashboard/ha-config-updates.ts index bc444e3d05..cc105120d9 100644 --- a/src/panels/config/dashboard/ha-config-updates.ts +++ b/src/panels/config/dashboard/ha-config-updates.ts @@ -64,7 +64,7 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) { const updates = this.updateEntities; return html` -
    +
    ${this.hass.localize("ui.panel.config.updates.title", { count: this.total || this.updateEntities.length, })} diff --git a/src/panels/config/repairs/ha-config-repairs.ts b/src/panels/config/repairs/ha-config-repairs.ts index 46c3a50816..156d559ce9 100644 --- a/src/panels/config/repairs/ha-config-repairs.ts +++ b/src/panels/config/repairs/ha-config-repairs.ts @@ -41,7 +41,7 @@ class HaConfigRepairs extends LitElement { const issues = this.repairsIssues; return html` -
    +
    ${this.hass.localize("ui.panel.config.repairs.title", { count: this.total || this.repairsIssues.length, })} From af149dcfab8f259322b647734dfb855f5a62d8a7 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 25 Jun 2025 16:18:47 +0200 Subject: [PATCH 119/216] Move exclude entities config to area card (#25909) --- .../hui-area-controls-card-feature.ts | 15 +++--- src/panels/lovelace/card-features/types.ts | 1 - src/panels/lovelace/cards/hui-area-card.ts | 32 +++++++++--- src/panels/lovelace/cards/types.ts | 1 + .../config-elements/hui-area-card-editor.ts | 52 ++++++++++++------- .../hui-area-controls-card-feature-editor.ts | 8 ++- .../areas/areas-overview-view-strategy.ts | 6 +-- 7 files changed, 77 insertions(+), 38 deletions(-) diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts index ceb789d02c..441684a477 100644 --- a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -18,6 +18,7 @@ import type { AreaRegistryEntry } from "../../../data/area_registry"; import { forwardHaptic } from "../../../data/haptics"; import { computeCssVariable } from "../../../resources/css-variables"; import type { HomeAssistant } from "../../../types"; +import type { AreaCardFeatureContext } from "../cards/hui-area-card"; import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types"; import { cardFeatureStyles } from "./common/card-feature-styles"; import type { @@ -86,8 +87,8 @@ export const supportsAreaControlsCardFeature = ( export const getAreaControlEntities = ( controls: AreaControl[], areaId: string, - hass: HomeAssistant, - excludeEntities: string[] = [] + excludeEntities: string[] | undefined, + hass: HomeAssistant ): Record => controls.reduce( (acc, control) => { @@ -99,7 +100,7 @@ export const getAreaControlEntities = ( }); acc[control] = Object.keys(hass.entities).filter( - (entityId) => filter(entityId) && !excludeEntities.includes(entityId) + (entityId) => filter(entityId) && !excludeEntities?.includes(entityId) ); return acc; }, @@ -115,7 +116,7 @@ class HuiAreaControlsCardFeature { @property({ attribute: false }) public hass?: HomeAssistant; - @property({ attribute: false }) public context?: LovelaceCardFeatureContext; + @property({ attribute: false }) public context?: AreaCardFeatureContext; @property({ attribute: false }) public position?: LovelaceCardFeaturePosition; @@ -168,7 +169,7 @@ class HuiAreaControlsCardFeature const controlEntities = this._controlEntities( this._controls, this.context.area_id, - this._config.exclude_entities, + this.context.exclude_entities, this.hass!.entities, this.hass!.devices, this.hass!.areas @@ -192,7 +193,7 @@ class HuiAreaControlsCardFeature _entities: HomeAssistant["entities"], _devices: HomeAssistant["devices"], _areas: HomeAssistant["areas"] - ) => getAreaControlEntities(controls, areaId, this.hass!, excludeEntities) + ) => getAreaControlEntities(controls, areaId, excludeEntities, this.hass!) ); protected render() { @@ -209,7 +210,7 @@ class HuiAreaControlsCardFeature const controlEntities = this._controlEntities( this._controls, this.context.area_id!, - this._config.exclude_entities, + this.context.exclude_entities, this.hass!.entities, this.hass!.devices, this.hass!.areas diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index 4b74481431..9ded542fe3 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -179,7 +179,6 @@ export type AreaControl = (typeof AREA_CONTROLS)[number]; export interface AreaControlsCardFeatureConfig { type: "area-controls"; controls?: AreaControl[]; - exclude_entities?: string[]; } export type LovelaceCardFeaturePosition = "bottom" | "inline"; diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index dc2844e731..1f545e3a78 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -53,6 +53,10 @@ export const DEVICE_CLASSES = { binary_sensor: ["motion", "moisture"], }; +export interface AreaCardFeatureContext extends LovelaceCardFeatureContext { + exclude_entities?: string[]; +} + @customElement("hui-area-card") export class HuiAreaCard extends LitElement implements LovelaceCard { @property({ attribute: false }) public hass!: HomeAssistant; @@ -61,7 +65,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { @state() private _config?: AreaCardConfig; - @state() private _featureContext: LovelaceCardFeatureContext = {}; + @state() private _featureContext: AreaCardFeatureContext = {}; private _ratio: { w: number; @@ -87,6 +91,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { this._featureContext = { area_id: config.area, + exclude_entities: config.exclude_entities, }; } @@ -166,7 +171,8 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { ( entities: HomeAssistant["entities"], areaId: string, - sensorClasses: string[] + sensorClasses: string[], + excludeEntities?: string[] ): Map => { const sensorFilter = generateEntityFilter(this.hass, { area: areaId, @@ -174,7 +180,10 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { domain: "sensor", device_class: sensorClasses, }); - const entityIds = Object.keys(entities).filter(sensorFilter); + const entityIds = Object.keys(entities).filter( + (id) => sensorFilter(id) && !excludeEntities?.includes(id) + ); + return this._groupEntitiesByDeviceClass(entityIds); } ); @@ -183,7 +192,8 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { ( entities: HomeAssistant["entities"], areaId: string, - binarySensorClasses: string[] + binarySensorClasses: string[], + excludeEntities?: string[] ): Map => { const binarySensorFilter = generateEntityFilter(this.hass, { area: areaId, @@ -191,7 +201,11 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { domain: "binary_sensor", device_class: binarySensorClasses, }); - const entityIds = Object.keys(entities).filter(binarySensorFilter); + + const entityIds = Object.keys(entities).filter( + (id) => binarySensorFilter(id) && !excludeEntities?.includes(id) + ); + return this._groupEntitiesByDeviceClass(entityIds); } ); @@ -215,13 +229,15 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { const areaId = this._config?.area; const area = areaId ? this.hass.areas[areaId] : undefined; const alertClasses = this._config?.alert_classes; + const excludeEntities = this._config?.exclude_entities; if (!area || !alertClasses) { return []; } const groupedEntities = this._groupedBinarySensorEntityIds( this.hass.entities, area.area_id, - alertClasses + alertClasses, + excludeEntities ); return ( @@ -286,6 +302,7 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { const areaId = this._config?.area; const area = areaId ? this.hass.areas[areaId] : undefined; const sensorClasses = this._config?.sensor_classes; + const excludeEntities = this._config?.exclude_entities; if (!area || !sensorClasses) { return undefined; } @@ -293,7 +310,8 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { const groupedEntities = this._groupedSensorEntityIds( this.hass.entities, area.area_id, - sensorClasses + sensorClasses, + excludeEntities ); const sensorStates = sensorClasses diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 38c58a6bed..0902cf853e 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -117,6 +117,7 @@ export interface AreaCardConfig extends LovelaceCardConfig { alert_classes?: string[]; features?: LovelaceCardFeatureConfig[]; features_position?: LovelaceCardFeaturePosition; + exclude_entities?: string[]; } export interface ButtonCardConfig extends LovelaceCardConfig { diff --git a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts index e256b21956..d0c20791e2 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-card-editor.ts @@ -32,7 +32,10 @@ import type { LovelaceCardFeatureConfig, LovelaceCardFeatureContext, } from "../../card-features/types"; -import { DEVICE_CLASSES } from "../../cards/hui-area-card"; +import { + DEVICE_CLASSES, + type AreaCardFeatureContext, +} from "../../cards/hui-area-card"; import type { AreaCardConfig } from "../../cards/types"; import type { LovelaceCardEditor } from "../../types"; import { baseLovelaceCardConfig } from "../structs/base-card-struct"; @@ -55,6 +58,7 @@ const cardConfigStruct = assign( features: optional(array(any())), features_position: optional(enums(["bottom", "inline"])), aspect_ratio: optional(string()), + exclude_entities: optional(array(string())), }) ); @@ -69,11 +73,7 @@ export class HuiAreaCardEditor @state() private _numericDeviceClasses?: string[]; - private _featureContext = memoizeOne( - (areaId?: string): LovelaceCardFeatureContext => ({ - area_id: areaId, - }) - ); + @state() private _featureContext: AreaCardFeatureContext = {}; private _schema = memoizeOne( ( @@ -174,7 +174,10 @@ export class HuiAreaCardEditor ); private _binaryClassesForArea = memoizeOne( - (area: string | undefined): string[] => { + ( + area: string | undefined, + excludeEntities: string[] | undefined + ): string[] => { if (!area) { return []; } @@ -186,7 +189,9 @@ export class HuiAreaCardEditor }); const classes = Object.keys(this.hass!.entities) - .filter(binarySensorFilter) + .filter( + (id) => binarySensorFilter(id) && !excludeEntities?.includes(id) + ) .map((id) => this.hass!.states[id]?.attributes.device_class) .filter((c): c is string => Boolean(c)); @@ -195,7 +200,11 @@ export class HuiAreaCardEditor ); private _sensorClassesForArea = memoizeOne( - (area: string | undefined, numericDeviceClasses?: string[]): string[] => { + ( + area: string | undefined, + excludeEntities: string[] | undefined, + numericDeviceClasses: string[] | undefined + ): string[] => { if (!area) { return []; } @@ -208,7 +217,7 @@ export class HuiAreaCardEditor }); const classes = Object.keys(this.hass!.entities) - .filter(sensorFilter) + .filter((id) => sensorFilter(id) && !excludeEntities?.includes(id)) .map((id) => this.hass!.states[id]?.attributes.device_class) .filter((c): c is string => Boolean(c)); @@ -257,6 +266,11 @@ export class HuiAreaCardEditor display_type: displayType, }; delete this._config.show_camera; + + this._featureContext = { + area_id: config.area, + exclude_entities: config.exclude_entities, + }; } protected async updated() { @@ -306,11 +320,13 @@ export class HuiAreaCardEditor return nothing; } - const areaId = this._config!.area; - - const possibleBinaryClasses = this._binaryClassesForArea(this._config.area); + const possibleBinaryClasses = this._binaryClassesForArea( + this._config.area, + this._config.exclude_entities + ); const possibleSensorClasses = this._sensorClassesForArea( this._config.area, + this._config.exclude_entities, this._numericDeviceClasses ); const binarySelectOptions = this._buildBinaryOptions( @@ -347,8 +363,9 @@ export class HuiAreaCardEditor ...this._config, }; - const featureContext = this._featureContext(areaId); - const hasCompatibleFeatures = this._hasCompatibleFeatures(featureContext); + const hasCompatibleFeatures = this._hasCompatibleFeatures( + this._featureContext + ); return html` ): void { const index = ev.detail.subElementConfig.index; const config = this._config!.features![index!]; - const featureContext = this._featureContext(this._config!.area); fireEvent(this, "edit-sub-element", { config: config, saveConfig: (newConfig) => this._updateFeature(index!, newConfig), - context: featureContext, + context: this._featureContext, type: "feature", } as EditSubElementEvent< LovelaceCardFeatureConfig, diff --git a/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts index 819ce72dfb..18089a26b7 100644 --- a/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-area-controls-card-feature-editor.ts @@ -17,8 +17,8 @@ import { AREA_CONTROLS, type AreaControl, type AreaControlsCardFeatureConfig, - type LovelaceCardFeatureContext, } from "../../card-features/types"; +import type { AreaCardFeatureContext } from "../../cards/hui-area-card"; import type { LovelaceCardFeatureEditor } from "../../types"; type AreaControlsCardFeatureData = AreaControlsCardFeatureConfig & { @@ -32,7 +32,7 @@ export class HuiAreaControlsCardFeatureEditor { @property({ attribute: false }) public hass?: HomeAssistant; - @property({ attribute: false }) public context?: LovelaceCardFeatureContext; + @property({ attribute: false }) public context?: AreaCardFeatureContext; @state() private _config?: AreaControlsCardFeatureConfig; @@ -78,6 +78,7 @@ export class HuiAreaControlsCardFeatureEditor private _supportedControls = memoizeOne( ( areaId: string, + excludeEntities: string[] | undefined, // needed to update memoized function when entities, devices or areas change _entities: HomeAssistant["entities"], _devices: HomeAssistant["devices"], @@ -89,6 +90,7 @@ export class HuiAreaControlsCardFeatureEditor const controlEntities = getAreaControlEntities( AREA_CONTROLS as unknown as AreaControl[], areaId, + excludeEntities, this.hass! ); return ( @@ -104,6 +106,7 @@ export class HuiAreaControlsCardFeatureEditor const supportedControls = this._supportedControls( this.context.area_id, + this.context.exclude_entities, this.hass.entities, this.hass.devices, this.hass.areas @@ -148,6 +151,7 @@ export class HuiAreaControlsCardFeatureEditor if (customize_controls && !config.controls) { config.controls = this._supportedControls( this.context!.area_id!, + this.context!.exclude_entities, this.hass!.entities, this.hass!.devices, this.hass!.areas diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 3ff37a0b7c..99feb34b5a 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -83,8 +83,8 @@ export class AreasOverviewViewStrategy extends ReactiveElement { const controlEntities = getAreaControlEntities( controls, area.area_id, - hass, - hiddenEntities + hiddenEntities, + hass ); const filteredControls = controls.filter( @@ -105,12 +105,12 @@ export class AreasOverviewViewStrategy extends ReactiveElement { "occupancy", "presence", ], + exclude_entities: hiddenEntities, features: filteredControls.length ? [ { type: "area-controls", controls: filteredControls, - exclude_entities: hiddenEntities, }, ] : [], From 93df473ad2df0d4ca64b29e54e054f5ce3a27e98 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Wed, 25 Jun 2025 18:18:29 +0300 Subject: [PATCH 120/216] Translate select options in config flows (#25911) --- src/components/ha-form/ha-form-select.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/ha-form/ha-form-select.ts b/src/components/ha-form/ha-form-select.ts index 8451b21a6c..5ccb28ba84 100644 --- a/src/components/ha-form/ha-form-select.ts +++ b/src/components/ha-form/ha-form-select.ts @@ -24,12 +24,16 @@ export class HaFormSelect extends LitElement implements HaFormElement { @property() public helper?: string; + @property({ attribute: false }) + public localizeValue?: (key: string) => string; + @property({ type: Boolean }) public disabled = false; private _selectSchema = memoizeOne( - (options): SelectSelector => ({ + (schema: HaFormSelectSchema): SelectSelector => ({ select: { - options: options.map((option) => ({ + translation_key: schema.name, + options: schema.options.map((option) => ({ value: option[0], label: option[1], })), @@ -41,13 +45,13 @@ export class HaFormSelect extends LitElement implements HaFormElement { return html` `; From 6006e926a7171d2e4ebbf2a3400889004d278df2 Mon Sep 17 00:00:00 2001 From: ildar170975 <71872483+ildar170975@users.noreply.github.com> Date: Wed, 25 Jun 2025 18:20:14 +0300 Subject: [PATCH 121/216] fix hui-panel-view for a "warning_multiple_cards" (#25899) --- src/panels/lovelace/views/hui-panel-view.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/panels/lovelace/views/hui-panel-view.ts b/src/panels/lovelace/views/hui-panel-view.ts index 83715e9a95..0145fed1b5 100644 --- a/src/panels/lovelace/views/hui-panel-view.ts +++ b/src/panels/lovelace/views/hui-panel-view.ts @@ -10,8 +10,8 @@ import type { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import type { HomeAssistant } from "../../../types"; import type { HuiCard } from "../cards/hui-card"; import type { HuiCardOptions } from "../components/hui-card-options"; -import type { HuiWarning } from "../components/hui-warning"; import type { Lovelace } from "../types"; +import "../../../components/ha-alert"; let editCodeLoaded = false; @@ -26,7 +26,7 @@ export class PanelView extends LitElement implements LovelaceViewElement { @property({ attribute: false }) public cards: HuiCard[] = []; - @state() private _card?: HuiCard | HuiWarning | HuiCardOptions; + @state() private _card?: HuiCard | HuiCardOptions; // eslint-disable-next-line @typescript-eslint/no-empty-function public setConfig(_config: LovelaceViewConfig): void {} @@ -63,11 +63,11 @@ export class PanelView extends LitElement implements LovelaceViewElement { protected render(): TemplateResult { return html` ${this.cards!.length > 1 - ? html` - ${this.hass!.localize( + ? html`${this.hass!.localize( "ui.panel.lovelace.editor.view.panel_mode.warning_multiple_cards" - )} - ` + )}` : ""} ${this._card} ${this.lovelace?.editMode && this.cards.length === 0 From e5bc234ab39926bd01f75a320a661fc0d955ded8 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Jun 2025 17:23:22 +0200 Subject: [PATCH 122/216] make debug mode better visible, improve disabling device (#25910) --- .../ha-config-entry-device-row.ts | 78 ++++++++++++++++++- .../ha-config-integration-page.ts | 23 ++++-- src/translations/en.json | 1 + 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/panels/config/integrations/ha-config-entry-device-row.ts b/src/panels/config/integrations/ha-config-entry-device-row.ts index 9bf576fb33..9401e42491 100644 --- a/src/panels/config/integrations/ha-config-entry-device-row.ts +++ b/src/panels/config/integrations/ha-config-entry-device-row.ts @@ -8,10 +8,16 @@ import { } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; import { getDeviceContext } from "../../../common/entity/context/get_device_context"; import { navigate } from "../../../common/navigate"; -import type { ConfigEntry } from "../../../data/config_entries"; +import { + disableConfigEntry, + type ConfigEntry, + type DisableConfigEntryResult, +} from "../../../data/config_entries"; import { removeConfigEntryFromDevice, updateDeviceRegistryEntry, @@ -26,7 +32,6 @@ import { } from "../../lovelace/custom-card-helpers"; import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail"; import "./ha-config-sub-entry-row"; -import { stopPropagation } from "../../../common/dom/stop_propagation"; @customElement("ha-config-entry-device-row") class HaConfigEntryDeviceRow extends LitElement { @@ -52,7 +57,7 @@ class HaConfigEntryDeviceRow extends LitElement { area ? area.name : undefined, ].filter(Boolean); - return html` + return html`
    ${computeDeviceNameDisplay(device, this.hass)}
    + dvc.id !== this.device.id && + dvc.config_entries.includes(this.entry.entry_id) + ) + ) { + const config_entry = this.entry; + if ( + config_entry && + !config_entry.disabled_by && + (await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.devices.confirm_disable_config_entry", + { entry_name: config_entry.title } + ), + confirmText: this.hass.localize("ui.common.yes"), + dismissText: this.hass.localize("ui.common.no"), + })) + ) { + let result: DisableConfigEntryResult; + try { + result = await disableConfigEntry(this.hass, this.entry.entry_id); + } catch (err: any) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_error" + ), + text: err.message, + }); + return; + } + if (result.require_restart) { + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.disable_restart_confirm" + ), + }); + } + return; + } + } + } + + if (disable) { + const confirm = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.device.confirm_disable", + { name: computeDeviceNameDisplay(this.device, this.hass) } + ), + confirmText: this.hass.localize("ui.common.yes"), + dismissText: this.hass.localize("ui.common.no"), + }); + + if (!confirm) { + return; + } + } + await updateDeviceRegistryEntry(this.hass, this.device.id, { - disabled_by: this.device.disabled_by === "user" ? null : "user", + disabled_by: disable ? "user" : null, }); } @@ -220,6 +287,9 @@ class HaConfigEntryDeviceRow extends LitElement { ha-md-list-item { --md-list-item-leading-space: 56px; } + .disabled { + opacity: 0.5; + } :host([narrow]) ha-md-list-item { --md-list-item-leading-space: 16px; } diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 148739aaaa..50334c6664 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -403,14 +403,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
    ` : nothing} - ${this._logInfo?.level === LogSeverity.DEBUG - ? html`
    - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.debug_logging_enabled" - )} -
    ` - : nothing} ${this._manifest?.iot_class?.startsWith("cloud_") ? html`
    @@ -539,6 +531,21 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
    + ${this._logInfo?.level === LogSeverity.DEBUG + ? html`
    + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.debug_logging_enabled" + )} + ${this.hass.localize("ui.common.disable")} + +
    ` + : nothing} ${discoveryFlows.length ? html`
    diff --git a/src/translations/en.json b/src/translations/en.json index ae3efff279..cb2e4be369 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5379,6 +5379,7 @@ "device": { "enable": "Enable device", "disable": "Disable device", + "confirm_disable": "Are you sure you want to disable {name}?", "configure": "Configure device", "delete": "Remove device" }, From edb1e1bba74986b02dd86196f0537e7e13afae0b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Jun 2025 17:24:02 +0200 Subject: [PATCH 123/216] Bumped version to 20250625.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 169b86dbe8..d6597ff9bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250430.0" +version = "20250625.0" license = "Apache-2.0" license-files = ["LICENSE*"] description = "The Home Assistant frontend" From eb9359e9e17db82de10ed8482946f7f13a6260a4 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Jun 2025 23:31:43 +0200 Subject: [PATCH 124/216] Only show own devices when there are devices... (#25920) only show own devices when there are devices... --- .../integrations/ha-config-entry-row.ts | 85 ++++++++++--------- .../ha-config-integration-page.ts | 3 +- 2 files changed, 47 insertions(+), 41 deletions(-) diff --git a/src/panels/config/integrations/ha-config-entry-row.ts b/src/panels/config/integrations/ha-config-entry-row.ts index 2d1c80a3fe..9fd08a91a1 100644 --- a/src/panels/config/integrations/ha-config-entry-row.ts +++ b/src/panels/config/integrations/ha-config-entry-row.ts @@ -405,47 +405,52 @@ class HaConfigEntryRow extends LitElement { ${this._expanded ? subEntries.length - ? html` - - + - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.devices_without_subentry" - )} - - ${this._devicesExpanded - ? ownDevices.map( - (device) => - html`` - ) - : nothing} - - ${subEntries.map( - (subEntry) => html` - - ` - )}` + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.devices_without_subentry" + )} + + ${this._devicesExpanded + ? ownDevices.map( + (device) => + html`` + ) + : nothing} + ` + : nothing} + ${subEntries.map( + (subEntry) => html` + + ` + )}` : html` ${ownDevices.map( (device) => diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 50334c6664..e3101c63ce 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -541,8 +541,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ${this.hass.localize("ui.common.disable")} + ${this.hass.localize("ui.common.disable")} +
    ` : nothing} From 551035238f4b89b5b500b8580c41f288ba5bee19 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Jun 2025 23:34:12 +0200 Subject: [PATCH 125/216] Dont show internal quality scale (#25921) dont show internal quality scale --- src/data/integration_quality_scale.ts | 6 ------ .../config/integrations/ha-config-integration-page.ts | 3 --- src/translations/en.json | 1 - 3 files changed, 10 deletions(-) diff --git a/src/data/integration_quality_scale.ts b/src/data/integration_quality_scale.ts index e49bbc835d..d4c06a0162 100644 --- a/src/data/integration_quality_scale.ts +++ b/src/data/integration_quality_scale.ts @@ -1,5 +1,4 @@ import { mdiContentSave, mdiMedal, mdiTrophy } from "@mdi/js"; -import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg"; import type { LocalizeKeys } from "../common/translations/localize"; /** @@ -26,11 +25,6 @@ export const QUALITY_SCALE_MAP: Record< translationKey: "ui.panel.config.integrations.config_entry.platinum_quality", }, - internal: { - icon: mdiHomeAssistant, - translationKey: - "ui.panel.config.integrations.config_entry.internal_integration", - }, legacy: { icon: mdiContentSave, translationKey: diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index e3101c63ce..9ff686a174 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -1008,9 +1008,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ha-svg-icon.platinum-quality { color: #727272; } - ha-svg-icon.internal-quality { - color: var(--primary-color); - } ha-svg-icon.legacy-quality { color: var(--mdc-theme-text-icon-on-background, rgba(0, 0, 0, 0.38)); animation: unset; diff --git a/src/translations/en.json b/src/translations/en.json index cb2e4be369..42f746f853 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5433,7 +5433,6 @@ } }, "custom_integration": "Custom integration", - "internal_integration": "Internal integration", "legacy_integration": "Legacy integration", "custom_overwrites_core": "Custom integration that replaces a core component", "depends_on_cloud": "Requires Internet", From 10eb0a8b874f1184b47572620f8c6111915724bf Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Thu, 26 Jun 2025 07:55:46 +0200 Subject: [PATCH 126/216] Deduplicate weekdays in time conditions (#25915) Replace weekdays conditions with references to common ui strings --- src/translations/en.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 42f746f853..c5147696fb 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4193,13 +4193,13 @@ "mode_after": "[%key:ui::panel::config::automation::editor::conditions::type::time::after%]", "mode_before": "[%key:ui::panel::config::automation::editor::conditions::type::time::before%]", "weekdays": { - "mon": "Monday", - "tue": "Tuesday", - "wed": "Wednesday", - "thu": "Thursday", - "fri": "Friday", - "sat": "Saturday", - "sun": "Sunday" + "mon": "[%key:ui::weekdays::monday%]", + "tue": "[%key:ui::weekdays::tuesday%]", + "wed": "[%key:ui::weekdays::wednesday%]", + "thu": "[%key:ui::weekdays::thursday%]", + "fri": "[%key:ui::weekdays::friday%]", + "sat": "[%key:ui::weekdays::saturday%]", + "sun": "[%key:ui::weekdays::sunday%]" }, "description": { "picker": "If the current time is before or after a specified time.", From 2dee45b465816ce60d9979e56b123f29d1861eb2 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 26 Jun 2025 07:57:16 +0200 Subject: [PATCH 127/216] Update confirm disable messages (#25919) --- src/panels/config/devices/ha-config-device-page.ts | 8 ++++++-- .../integrations/ha-config-entry-device-row.ts | 14 +++++++++++--- src/translations/en.json | 6 ++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 4220645aaa..34f511b9c0 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -1317,9 +1317,13 @@ export class HaConfigDevicePage extends LitElement { // eslint-disable-next-line no-await-in-loop (await showConfirmationDialog(this, { title: this.hass.localize( - "ui.panel.config.devices.confirm_disable_config_entry", - { entry_name: config_entry.title } + "ui.panel.config.devices.confirm_disable_config_entry_title" ), + text: this.hass.localize( + "ui.panel.config.devices.confirm_disable_config_entry_message", + { name: config_entry.title } + ), + destructive: true, confirmText: this.hass.localize("ui.common.yes"), dismissText: this.hass.localize("ui.common.no"), })) diff --git a/src/panels/config/integrations/ha-config-entry-device-row.ts b/src/panels/config/integrations/ha-config-entry-device-row.ts index 9401e42491..58dfc11da7 100644 --- a/src/panels/config/integrations/ha-config-entry-device-row.ts +++ b/src/panels/config/integrations/ha-config-entry-device-row.ts @@ -196,9 +196,13 @@ class HaConfigEntryDeviceRow extends LitElement { !config_entry.disabled_by && (await showConfirmationDialog(this, { title: this.hass.localize( - "ui.panel.config.devices.confirm_disable_config_entry", - { entry_name: config_entry.title } + "ui.panel.config.devices.confirm_disable_config_entry_title" ), + text: this.hass.localize( + "ui.panel.config.devices.confirm_disable_config_entry_message", + { name: config_entry.title } + ), + destructive: true, confirmText: this.hass.localize("ui.common.yes"), dismissText: this.hass.localize("ui.common.no"), })) @@ -230,9 +234,13 @@ class HaConfigEntryDeviceRow extends LitElement { if (disable) { const confirm = await showConfirmationDialog(this, { title: this.hass.localize( - "ui.panel.config.integrations.config_entry.device.confirm_disable", + "ui.panel.config.integrations.config_entry.device.confirm_disable_title" + ), + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.device.confirm_disable_message", { name: computeDeviceNameDisplay(this.device, this.hass) } ), + destructive: true, confirmText: this.hass.localize("ui.common.yes"), dismissText: this.hass.localize("ui.common.no"), }); diff --git a/src/translations/en.json b/src/translations/en.json index c5147696fb..43950aea5c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5128,7 +5128,8 @@ "disabled_entities": "+{count} disabled {count, plural,\n one {entity}\n other {entities}\n}", "hidden": "Hidden" }, - "confirm_disable_config_entry": "There are no more devices for the config entry {entry_name}, do you want to instead disable the config entry?", + "confirm_disable_config_entry_title": "Disable config entry?", + "confirm_disable_config_entry_message": "There are no more devices for the config entry {name}, do you want to instead disable the config entry?", "update_device_error": "Updating the device failed", "disabled": "Disabled", "data_table": { @@ -5379,7 +5380,8 @@ "device": { "enable": "Enable device", "disable": "Disable device", - "confirm_disable": "Are you sure you want to disable {name}?", + "confirm_disable_title": "Disable device?", + "confirm_disable_message": "Are you sure you want to disable {name} and all of it’s entities?", "configure": "Configure device", "delete": "Remove device" }, From 34112e7446d45e4e818bdaf7a0fbd90c0cf91a6e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 Jun 2025 03:41:10 -0400 Subject: [PATCH 128/216] Fix wrapping of add subentry buttons (#25925) --- src/panels/config/integrations/ha-config-integration-page.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 9ff686a174..639255962a 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -954,6 +954,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { } .actions { display: flex; + flex-wrap: wrap; gap: 8px; } .section { From b81cd37776dff1b4d7c62d894df0fa5ed6f4748f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 26 Jun 2025 03:41:39 -0400 Subject: [PATCH 129/216] Make the config entry row section wider on mobile (#25924) --- src/panels/config/integrations/ha-config-entry-row.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/panels/config/integrations/ha-config-entry-row.ts b/src/panels/config/integrations/ha-config-entry-row.ts index 9fd08a91a1..d04f38d056 100644 --- a/src/panels/config/integrations/ha-config-entry-row.ts +++ b/src/panels/config/integrations/ha-config-entry-row.ts @@ -745,6 +745,10 @@ class HaConfigEntryRow extends LitElement { border-radius: var(--ha-card-border-radius, 12px); padding: 0; } + :host([narrow]) { + margin-left: -12px; + margin-right: -12px; + } ha-md-list.devices { margin: 16px; margin-top: 0; From dd4cb1df72e071beefe135143f97b890fb4e64b1 Mon Sep 17 00:00:00 2001 From: Eric Stern Date: Thu, 26 Jun 2025 00:42:43 -0700 Subject: [PATCH 130/216] Fix logbook stream subscription (#25927) --- src/data/logbook.ts | 8 ++++++-- src/panels/logbook/ha-logbook.ts | 11 ++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/data/logbook.ts b/src/data/logbook.ts index d57e27fd26..21822d0916 100644 --- a/src/data/logbook.ts +++ b/src/data/logbook.ts @@ -114,9 +114,13 @@ const getLogbookDataFromServer = ( export const subscribeLogbook = ( hass: HomeAssistant, - callbackFunction: (message: LogbookStreamMessage) => void, + callbackFunction: ( + message: LogbookStreamMessage, + subscriptionId: number + ) => void, startDate: string, endDate: string, + subscriptionId: number, entityIds?: string[], deviceIds?: string[] ): Promise => { @@ -140,7 +144,7 @@ export const subscribeLogbook = ( params.device_ids = deviceIds; } return hass.connection.subscribeMessage( - (message) => callbackFunction(message), + (message) => callbackFunction(message, subscriptionId), params ); }; diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 2f404ed0aa..230e8fd905 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -88,6 +88,8 @@ export class HaLogbook extends LitElement { 1000 ); + private _logbookSubscriptionId = 0; + protected render() { if (!isComponentLoaded(this.hass, "logbook")) { return nothing; @@ -278,13 +280,20 @@ export class HaLogbook extends LitElement { } try { + this._logbookSubscriptionId++; + this._unsubLogbook = subscribeLogbook( this.hass, - (streamMessage) => { + (streamMessage, subscriptionId) => { + if (subscriptionId !== this._logbookSubscriptionId) { + // Ignore messages from previous subscriptions + return; + } this._processOrQueueStreamMessage(streamMessage); }, logbookPeriod.startTime.toISOString(), logbookPeriod.endTime.toISOString(), + this._logbookSubscriptionId, this.entityIds, this.deviceIds ); From 89ab0b4a3d43473357b18bb7131ab8b669508e03 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:18:03 +0200 Subject: [PATCH 131/216] Update dependency prettier to v3.6.0 (#25930) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index d9266af1d0..c549c13796 100644 --- a/package.json +++ b/package.json @@ -210,7 +210,7 @@ "lodash.template": "4.5.0", "map-stream": "0.0.7", "pinst": "3.0.0", - "prettier": "3.5.3", + "prettier": "3.6.0", "rspack-manifest-plugin": "5.0.3", "serve": "14.2.4", "sinon": "21.0.0", diff --git a/yarn.lock b/yarn.lock index 9744bd65f3..8146b5b538 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9406,7 +9406,7 @@ __metadata: node-vibrant: "npm:4.0.3" object-hash: "npm:3.0.0" pinst: "npm:3.0.0" - prettier: "npm:3.5.3" + prettier: "npm:3.6.0" punycode: "npm:2.3.1" qr-scanner: "npm:1.4.2" qrcode: "npm:1.5.4" @@ -12245,12 +12245,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.5.3": - version: 3.5.3 - resolution: "prettier@npm:3.5.3" +"prettier@npm:3.6.0": + version: 3.6.0 + resolution: "prettier@npm:3.6.0" bin: prettier: bin/prettier.cjs - checksum: 10/7050c08f674d9e49fbd9a4c008291d0715471f64e94cc5e4b01729affce221dfc6875c8de7e66b728c64abc9352eefb7eaae071b5f79d30081be207b53774b78 + checksum: 10/5c0db5a8e32d2ac9824d8bc652990dfd534bc7a7c6f26d99d50c9146a2d9befb3cd1cc86c4aee71caf6b264d421a4b4b5961e31a62dda3790b8fec2521a76eef languageName: node linkType: hard From d8ab9b73ba0f8083a2521b6635bdbe242355433f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 26 Jun 2025 12:17:00 +0200 Subject: [PATCH 132/216] Prevent overflow of ripple on device row on integration page (#25922) --- src/panels/config/integrations/ha-config-entry-row.ts | 11 +++++++++++ src/translations/en.json | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/panels/config/integrations/ha-config-entry-row.ts b/src/panels/config/integrations/ha-config-entry-row.ts index d04f38d056..afee3d8efb 100644 --- a/src/panels/config/integrations/ha-config-entry-row.ts +++ b/src/panels/config/integrations/ha-config-entry-row.ts @@ -410,6 +410,9 @@ class HaConfigEntryRow extends LitElement { Date: Thu, 26 Jun 2025 12:18:34 +0200 Subject: [PATCH 133/216] Better handle case when no floors in areas dashboard (#25933) --- .../ha-areas-floors-display-editor.ts | 55 +++++++++++-------- .../areas/areas-overview-view-strategy.ts | 29 ++++++---- src/translations/en.json | 3 +- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/components/ha-areas-floors-display-editor.ts b/src/components/ha-areas-floors-display-editor.ts index 414e2550c7..03ecec9ac2 100644 --- a/src/components/ha-areas-floors-display-editor.ts +++ b/src/components/ha-areas-floors-display-editor.ts @@ -1,6 +1,6 @@ import { mdiTextureBox } from "@mdi/js"; import type { TemplateResult } from "lit"; -import { LitElement, css, html } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; @@ -64,26 +64,30 @@ export class HaAreasFloorsDisplayEditor extends LitElement { .expanded=${this.expanded} > - ${filteredFloors.map( - (floor) => html` + ${filteredFloors.map((floor, _, array) => { + const noFloors = + array.length === 1 && floor.floor_id === UNASSIGNED_FLOOR; + return html`
    -
    - -

    ${computeFloorName(floor)}

    -
    + ${noFloors + ? nothing + : html`
    + +

    ${computeFloorName(floor)}

    +
    `}
    - ` - )} + `; + })} `; } @@ -134,10 +138,10 @@ export class HaAreasFloorsDisplayEditor extends LitElement { floors.push({ floor_id: UNASSIGNED_FLOOR, name: this.hass.localize( - "ui.panel.lovelace.strategy.areas.unassigned_areas" + "ui.panel.lovelace.strategy.areas.others_areas" ), icon: null, - level: 999999, + level: null, aliases: [], created_at: 0, modified_at: 0, @@ -155,25 +159,30 @@ export class HaAreasFloorsDisplayEditor extends LitElement { (floor) => floor.floor_id ); + const oldHidden = this.value?.hidden ?? []; + const oldOrder = this.value?.order ?? []; + const newHidden: string[] = []; const newOrder: string[] = []; for (const floorId of floorIds) { - if (currentFloorId === floorId) { + if ((currentFloorId ?? UNASSIGNED_FLOOR) === floorId) { newHidden.push(...(value.hidden ?? [])); newOrder.push(...(value.order ?? [])); continue; } - const hidden = this.value?.hidden?.filter( - (areaId) => this.hass.areas[areaId]?.floor_id === floorId - ); - if (hidden) { + const hidden = oldHidden.filter((areaId) => { + const id = this.hass.areas[areaId]?.floor_id ?? UNASSIGNED_FLOOR; + return id === floorId; + }); + if (hidden?.length) { newHidden.push(...hidden); } - const order = this.value?.order?.filter( - (areaId) => this.hass.areas[areaId]?.floor_id === floorId - ); - if (order) { + const order = oldOrder.filter((areaId) => { + const id = this.hass.areas[areaId]?.floor_id ?? UNASSIGNED_FLOOR; + return id === floorId; + }); + if (order?.length) { newOrder.push(...order); } } @@ -188,7 +197,7 @@ export class HaAreasFloorsDisplayEditor extends LitElement { if (newValue.order?.length === 0) { delete newValue.order; } - + this.value = newValue; fireEvent(this, "value-changed", { value: newValue }); } @@ -199,7 +208,7 @@ export class HaAreasFloorsDisplayEditor extends LitElement { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - flex: 1: + flex: 1; } .floor .header { margin: 16px 0 8px 0; diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 99feb34b5a..0d0e43e8c9 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -32,7 +32,7 @@ export class AreasOverviewViewStrategy extends ReactiveElement { config: AreasViewStrategyConfig, hass: HomeAssistant ): Promise { - const areas = getAreas( + const displayedAreas = getAreas( hass.areas, config.areas_display?.hidden, config.areas_display?.order @@ -50,25 +50,23 @@ export class AreasOverviewViewStrategy extends ReactiveElement { ...floors, { floor_id: UNASSIGNED_FLOOR, - name: hass.localize( - "ui.panel.lovelace.strategy.areas.unassigned_areas" - ), + name: hass.localize("ui.panel.lovelace.strategy.areas.others_areas"), level: null, icon: null, }, ] - .map((floor) => { - const areasInFloors = areas.filter( + .map((floor) => { + const areasInFloors = displayedAreas.filter( (area) => area.floor_id === floor.floor_id || (!area.floor_id && floor.floor_id === UNASSIGNED_FLOOR) ); - if (areasInFloors.length === 0) { - return undefined; - } - - const areasCards = areasInFloors.map((area) => { + return [floor, areasInFloors] as const; + }) + .filter(([_, areas]) => areas.length) + .map(([floor, areas], _, array) => { + const areasCards = areas.map((area) => { const path = computeAreaPath(area.area_id); const areaOptions = config.areas_options?.[area.area_id] || {}; @@ -123,10 +121,17 @@ export class AreasOverviewViewStrategy extends ReactiveElement { }; }); + const noFloors = + array.length === 1 && floor.floor_id === UNASSIGNED_FLOOR; + + const headingTitle = noFloors + ? hass.localize("ui.panel.lovelace.strategy.areas.areas") + : floor.name; + const headingCard: HeadingCardConfig = { type: "heading", heading_style: "title", - heading: floor.name, + heading: headingTitle, icon: floor.icon || floorDefaultIcon(floor), }; diff --git a/src/translations/en.json b/src/translations/en.json index ef0fe838d7..8b28a90f5a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6685,7 +6685,8 @@ "actions": "Actions", "others": "Others" }, - "unassigned_areas": "[%key:ui::panel::config::areas::picker::unassigned_areas%]" + "others_areas": "Other areas", + "areas": "Areas" } }, "cards": { From 675310afdff68b630a413daa16aa3cc9cceca00f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 26 Jun 2025 13:04:13 +0200 Subject: [PATCH 134/216] add version number to integration page (#25940) * add version number to integration page * Update ha-config-integration-page.ts --- .../config/integrations/ha-config-integration-page.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 639255962a..8b6f5c0d56 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -380,6 +380,11 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {

    ${domainToName(this.hass.localize, this.domain)}

    + ${this._manifest?.version != null + ? html`${this._manifest.version}` + : nothing} ${this._manifest?.is_built_in === false ? html`
    Date: Thu, 26 Jun 2025 13:05:05 +0200 Subject: [PATCH 135/216] Remove alert classes and only use slot sensors for areas dashboard (#25937) * Remove alert classes and only used slot sensors for areas dashboard * Rename group to sensors * Rename group to sensors --- .../ha-areas-floors-display-editor.ts | 7 ++++ .../areas/areas-overview-view-strategy.ts | 19 +++++----- .../hui-areas-dashboard-strategy-editor.ts | 36 ++++++++++++------- src/translations/en.json | 4 +-- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/components/ha-areas-floors-display-editor.ts b/src/components/ha-areas-floors-display-editor.ts index 03ecec9ac2..cd56b94ffc 100644 --- a/src/components/ha-areas-floors-display-editor.ts +++ b/src/components/ha-areas-floors-display-editor.ts @@ -218,6 +218,13 @@ export class HaAreasFloorsDisplayEditor extends LitElement { align-items: center; gap: 8px; } + ha-expansion-panel { + margin-bottom: 8px; + --expansion-panel-summary-padding: 0 16px; + } + ha-expansion-panel [slot="leading-icon"] { + margin-inline-end: 16px; + } `; } diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 0d0e43e8c9..9c47f38ddd 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -89,20 +89,19 @@ export class AreasOverviewViewStrategy extends ReactiveElement { (control) => controlEntities[control].length > 0 ); + const sensorClasses: string[] = []; + if (area.temperature_entity_id) { + sensorClasses.push("temperature"); + } + if (area.humidity_entity_id) { + sensorClasses.push("humidity"); + } + return { type: "area", area: area.area_id, display_type: "compact", - sensor_classes: ["temperature", "humidity"], - alert_classes: [ - "water_leak", - "smoke", - "gas", - "co", - "motion", - "occupancy", - "presence", - ], + sensor_classes: sensorClasses, exclude_entities: hiddenEntities, features: filteredControls.length ? [ diff --git a/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts b/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts index 311641caa2..73e7d890bb 100644 --- a/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts +++ b/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts @@ -1,28 +1,30 @@ +import { mdiThermometerWater } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/ha-areas-display-editor"; import type { AreasDisplayValue } from "../../../../../components/ha-areas-display-editor"; +import "../../../../../components/ha-areas-floors-display-editor"; import "../../../../../components/ha-entities-display-editor"; +import "../../../../../components/ha-icon"; import "../../../../../components/ha-icon-button"; import "../../../../../components/ha-icon-button-prev"; -import "../../../../../components/ha-icon"; +import "../../../../../components/ha-svg-icon"; +import { + updateAreaRegistryEntry, + type AreaRegistryEntry, +} from "../../../../../data/area_registry"; +import { buttonLinkStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; +import { showAreaRegistryDetailDialog } from "../../../../config/areas/show-dialog-area-registry-detail"; +import type { LovelaceStrategyEditor } from "../../types"; +import type { AreasDashboardStrategyConfig } from "../areas-dashboard-strategy"; import type { AreaStrategyGroup } from "../helpers/areas-strategy-helper"; import { AREA_STRATEGY_GROUP_ICONS, AREA_STRATEGY_GROUPS, getAreaGroupedEntities, } from "../helpers/areas-strategy-helper"; -import type { LovelaceStrategyEditor } from "../../types"; -import type { AreasDashboardStrategyConfig } from "../areas-dashboard-strategy"; -import { showAreaRegistryDetailDialog } from "../../../../config/areas/show-dialog-area-registry-detail"; -import { - updateAreaRegistryEntry, - type AreaRegistryEntry, -} from "../../../../../data/area_registry"; -import { buttonLinkStyle } from "../../../../../resources/styles"; -import "../../../../../components/ha-areas-floors-display-editor"; @customElement("hui-areas-dashboard-strategy-editor") export class HuiAreasDashboardStrategyEditor @@ -58,14 +60,18 @@ export class HuiAreasDashboardStrategyEditor
    +

    ${this.hass!.localize( - `ui.panel.lovelace.strategy.areas.header_description`, + `ui.panel.lovelace.strategy.areas.sensors_description`, { edit_the_area: html`

    ${supportingText.join(" • ")} From 2fb86f118ef124c2a6a3b8cb461a69afebb33788 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 26 Jun 2025 13:07:08 +0200 Subject: [PATCH 137/216] make sure header is always shown in data entry flow (#25941) --- src/dialogs/config-flow/dialog-data-entry-flow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/config-flow/dialog-data-entry-flow.ts b/src/dialogs/config-flow/dialog-data-entry-flow.ts index 298f5742e2..2a03334b9b 100644 --- a/src/dialogs/config-flow/dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/dialog-data-entry-flow.ts @@ -286,7 +286,7 @@ class DataEntryFlowDialog extends LitElement { scrimClickAction escapeKeyAction hideActions - .heading=${dialogTitle} + .heading=${dialogTitle || true} > Date: Thu, 26 Jun 2025 14:38:33 +0200 Subject: [PATCH 138/216] Add label to version number (#25942) Add label --- src/panels/config/integrations/ha-config-integration-page.ts | 5 ++++- src/translations/en.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 8b6f5c0d56..4b11f37d86 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -382,7 +382,10 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
    ${this._manifest?.version != null ? html`${this._manifest.version}${this.hass.localize( + "ui.panel.config.integrations.config_entry.version", + { version: this._manifest.version } + )}` : nothing} ${this._manifest?.is_built_in === false diff --git a/src/translations/en.json b/src/translations/en.json index 5317965c7e..6c5bc5becf 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5416,7 +5416,7 @@ "via": "Connected via", "firmware": "Firmware: {version}", "hardware": "Hardware: {version}", - "version": "Version: {version}", + "version": "Version {version}", "serial_number": "Serial number: {serial_number}", "unnamed_entry": "Unnamed entry", "unknown_via_device": "Unknown device", From a952b880d8da39ff0f05b7a8140e05474575ef4f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 26 Jun 2025 15:25:26 +0200 Subject: [PATCH 139/216] Disable escape key to close edit card dialog (#25947) --- .../card-editor/hui-dialog-edit-card.ts | 19 +------------------ 1 file changed, 1 insertion(+), 18 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 ed1a507966..a72abfec65 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 @@ -74,8 +74,6 @@ export class HuiDialogEditCard @state() private _dirty = false; - @state() private _isEscapeEnabled = true; - public async showDialog(params: EditCardDialogParams): Promise { this._params = params; this._GUImode = true; @@ -93,9 +91,6 @@ export class HuiDialogEditCard } public closeDialog(): boolean { - this._isEscapeEnabled = true; - window.removeEventListener("dialog-closed", this._enableEscapeKeyClose); - window.removeEventListener("hass-more-info", this._disableEscapeKeyClose); if (this._dirty) { this._confirmCancel(); return false; @@ -124,16 +119,6 @@ export class HuiDialogEditCard } } - private _enableEscapeKeyClose = (ev: any) => { - if (ev.detail.dialog === "ha-more-info-dialog") { - this._isEscapeEnabled = true; - } - }; - - private _disableEscapeKeyClose = () => { - this._isEscapeEnabled = false; - }; - protected render() { if (!this._params || !this._cardConfig) { return nothing; @@ -170,7 +155,7 @@ export class HuiDialogEditCard Date: Thu, 26 Jun 2025 16:07:46 +0200 Subject: [PATCH 140/216] Load title when fetching flow (#25951) --- src/dialogs/config-flow/show-dialog-config-flow.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/dialogs/config-flow/show-dialog-config-flow.ts b/src/dialogs/config-flow/show-dialog-config-flow.ts index 2976159f6f..346590e42e 100644 --- a/src/dialogs/config-flow/show-dialog-config-flow.ts +++ b/src/dialogs/config-flow/show-dialog-config-flow.ts @@ -35,10 +35,16 @@ export const showConfigFlowDialog = ( return step; }, fetchFlow: async (hass, flowId) => { - const step = await fetchConfigFlow(hass, flowId); - await hass.loadFragmentTranslation("config"); - await hass.loadBackendTranslation("config", step.handler); - await hass.loadBackendTranslation("selector", step.handler); + const [step] = await Promise.all([ + fetchConfigFlow(hass, flowId), + hass.loadFragmentTranslation("config"), + ]); + await Promise.all([ + hass.loadBackendTranslation("config", step.handler), + hass.loadBackendTranslation("selector", step.handler), + // Used as fallback if no header defined for step + hass.loadBackendTranslation("title", step.handler), + ]); return step; }, handleFlowStep: handleConfigFlowStep, From e2ab52e10e8bc7b70bf133dcf62c099198f9ce24 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 26 Jun 2025 16:12:07 +0200 Subject: [PATCH 141/216] Don't limit combo-box dropdown size (#25952) --- src/components/ha-combo-box.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ha-combo-box.ts b/src/components/ha-combo-box.ts index bcdc9077e7..44e55c55d9 100644 --- a/src/components/ha-combo-box.ts +++ b/src/components/ha-combo-box.ts @@ -369,7 +369,6 @@ export class HaComboBox extends LitElement { } vaadin-combo-box-light { position: relative; - --vaadin-combo-box-overlay-max-height: calc(45vh - 56px); } ha-combo-box-textfield { width: 100%; From 696ba69a9eb77e4d3ef71a6dd9fe45fd5608afea Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 26 Jun 2025 16:41:58 +0200 Subject: [PATCH 142/216] Revert vaadin to 24.7.7 (#25953) --- package.json | 4 +- yarn.lock | 188 ++++++++++++++++++++++++++------------------------- 2 files changed, 97 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index c549c13796..af35e02e77 100644 --- a/package.json +++ b/package.json @@ -89,8 +89,8 @@ "@thomasloven/round-slider": "0.6.0", "@tsparticles/engine": "3.8.1", "@tsparticles/preset-links": "3.2.0", - "@vaadin/combo-box": "24.8.0", - "@vaadin/vaadin-themable-mixin": "24.8.0", + "@vaadin/combo-box": "24.7.7", + "@vaadin/vaadin-themable-mixin": "24.7.7", "@vibrant/color": "4.0.0", "@vue/web-component-wrapper": "1.3.0", "@webcomponents/scoped-custom-element-registry": "0.0.10", diff --git a/yarn.lock b/yarn.lock index 8146b5b538..4dca964822 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5017,131 +5017,131 @@ __metadata: languageName: node linkType: hard -"@vaadin/a11y-base@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/a11y-base@npm:24.8.0" +"@vaadin/a11y-base@npm:~24.7.7, @vaadin/a11y-base@npm:~24.7.9": + version: 24.7.9 + resolution: "@vaadin/a11y-base@npm:24.7.9" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/component-base": "npm:~24.7.9" lit: "npm:^3.0.0" - checksum: 10/67aa4700b9f385aa92ca18e68abaa2fb592336673f2286dd2ef2b5eaba1fb8b882f454590fb68e975d4caf0a8698a953ffddcece943d10f91b960272152db564 + checksum: 10/7ea96ca92f292a899e0a911f190d194eb330f2916c2dfdfc5de83771b6f339af2413e8f8f8909ac66b2bd876854cc5e9043484062c817e997ef72fc467af3709 languageName: node linkType: hard -"@vaadin/combo-box@npm:24.8.0": - version: 24.8.0 - resolution: "@vaadin/combo-box@npm:24.8.0" +"@vaadin/combo-box@npm:24.7.7": + version: 24.7.7 + resolution: "@vaadin/combo-box@npm:24.7.7" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:^24.8.0" - "@vaadin/component-base": "npm:^24.8.0" - "@vaadin/field-base": "npm:^24.8.0" - "@vaadin/input-container": "npm:^24.8.0" - "@vaadin/item": "npm:^24.8.0" - "@vaadin/lit-renderer": "npm:^24.8.0" - "@vaadin/overlay": "npm:^24.8.0" - "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" - "@vaadin/vaadin-material-styles": "npm:^24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" + "@vaadin/a11y-base": "npm:~24.7.7" + "@vaadin/component-base": "npm:~24.7.7" + "@vaadin/field-base": "npm:~24.7.7" + "@vaadin/input-container": "npm:~24.7.7" + "@vaadin/item": "npm:~24.7.7" + "@vaadin/lit-renderer": "npm:~24.7.7" + "@vaadin/overlay": "npm:~24.7.7" + "@vaadin/vaadin-lumo-styles": "npm:~24.7.7" + "@vaadin/vaadin-material-styles": "npm:~24.7.7" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.7" lit: "npm:^3.0.0" - checksum: 10/c448e127ec53abfd3beca525518342a6d61ad62874e5c8ed3dea56cab66b7f9e132d40d6727ebe42dad53d0b343d30405ca6d9018d3a3a8b7b96c1f50b6d0133 + checksum: 10/ac9af96785d03ede12ac85c6a00f94a2122fc85db168ca2669a716138bc99a7df29630d4ede3274a6a9e97b53eac074c2f86480be0c3468d1b0118a4aca2aecd languageName: node linkType: hard -"@vaadin/component-base@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/component-base@npm:24.8.0" +"@vaadin/component-base@npm:~24.7.7, @vaadin/component-base@npm:~24.7.9": + version: 24.7.9 + resolution: "@vaadin/component-base@npm:24.7.9" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" "@vaadin/vaadin-development-mode-detector": "npm:^2.0.0" "@vaadin/vaadin-usage-statistics": "npm:^2.1.0" lit: "npm:^3.0.0" - checksum: 10/7111877c340af80fceb9042d3a58f7916579b0c6639268fa899bc107f61c357b65154e07247232ca9f922ba3399c43b1176fa2226f4faa1091f51ab11c74c572 + checksum: 10/24c11b6d395978b82ff54503dc578ef89ce6b2644d2768f1f25ec058b921ab3f9e3d011bf9a739db30112a40c1f89f61ec2cec41c2c0031a603f3c484c6ead11 languageName: node linkType: hard -"@vaadin/field-base@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/field-base@npm:24.8.0" +"@vaadin/field-base@npm:~24.7.7": + version: 24.7.9 + resolution: "@vaadin/field-base@npm:24.7.9" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:^24.8.0" - "@vaadin/component-base": "npm:^24.8.0" + "@vaadin/a11y-base": "npm:~24.7.9" + "@vaadin/component-base": "npm:~24.7.9" lit: "npm:^3.0.0" - checksum: 10/3357d43a310464d9c76f10e782ec658ae5af9b5a9308faf6de878e3e110b4c20ebc6af8b7f33954f73cfd0f527798787c1994498675067cc814c01ef0588deeb + checksum: 10/4c93e46621871daace3a202e33e5da0f8021c5f3847675ebc608e813a2c2a466a8f5743288eb591296b5119f2735bb18c754e360928b179221271ecae943f240 languageName: node linkType: hard -"@vaadin/icon@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/icon@npm:24.8.0" +"@vaadin/icon@npm:~24.7.9": + version: 24.7.9 + resolution: "@vaadin/icon@npm:24.7.9" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:^24.8.0" - "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" + "@vaadin/component-base": "npm:~24.7.9" + "@vaadin/vaadin-lumo-styles": "npm:~24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.9" lit: "npm:^3.0.0" - checksum: 10/e0a90e694179d59228c32e8e93c89e0cc2ebc9509995d6a24679137086796a116b6de9c7fd2e64cec532445bbb20a9991f958d6482cedbf271d041e611163558 + checksum: 10/277156010b88541b7cf473683899c0c2004d049f98a1aeb76555bf0e728663193d273a4224d88a04c1eabefbd6710c7a77f11c5b01c3e1037ef1b95c9c2c5ffc languageName: node linkType: hard -"@vaadin/input-container@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/input-container@npm:24.8.0" +"@vaadin/input-container@npm:~24.7.7": + version: 24.7.9 + resolution: "@vaadin/input-container@npm:24.7.9" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:^24.8.0" - "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" - "@vaadin/vaadin-material-styles": "npm:^24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" + "@vaadin/component-base": "npm:~24.7.9" + "@vaadin/vaadin-lumo-styles": "npm:~24.7.9" + "@vaadin/vaadin-material-styles": "npm:~24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.9" lit: "npm:^3.0.0" - checksum: 10/b9ae6590936f68cfb77d5d4ff29b7acb63c16482059391efd64f72837984df084aeb6f39313adb3ea482496adb6f3d84a3195dd265d59f508ccffcb0d5d7b9a4 + checksum: 10/6cc5934626c056178ba35bbe21a4f4094591e40955931630fc7a00c7a3db89be59b6884a75ef956b6f39eab1ced4309bffed9f57f084775b73e5d8b7a27c4ed7 languageName: node linkType: hard -"@vaadin/item@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/item@npm:24.8.0" +"@vaadin/item@npm:~24.7.7": + version: 24.7.9 + resolution: "@vaadin/item@npm:24.7.9" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:^24.8.0" - "@vaadin/component-base": "npm:^24.8.0" - "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" - "@vaadin/vaadin-material-styles": "npm:^24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" + "@vaadin/a11y-base": "npm:~24.7.9" + "@vaadin/component-base": "npm:~24.7.9" + "@vaadin/vaadin-lumo-styles": "npm:~24.7.9" + "@vaadin/vaadin-material-styles": "npm:~24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.9" lit: "npm:^3.0.0" - checksum: 10/a4eb618e7d204147379732244fc5bf3cdf7420ce168f91e0db9cb2a809fad267af4f54eac117a7fbb287a33489a12c7d4724d3a4e7a7fdd415ac30eb6f3554b5 + checksum: 10/d0317af3876686cc9353fe42d582f3b03ebb0d3f7f6c7760f95ac8f8e50b7995abccb1804e1fc2df58265b710eab53a881ed07e52c0a716d4da84e275560c95f languageName: node linkType: hard -"@vaadin/lit-renderer@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/lit-renderer@npm:24.8.0" +"@vaadin/lit-renderer@npm:~24.7.7": + version: 24.7.9 + resolution: "@vaadin/lit-renderer@npm:24.7.9" dependencies: lit: "npm:^3.0.0" - checksum: 10/f843d389f8f1958ec70d5180535a620e774f90bd3e3e5e77e41c6b89f4820272df2a68a70a5d6d752dbb3078b966985767d8ca079054a5fc0daa95d000524d27 + checksum: 10/a2101e428a537537e63be12f151f59fb70ae47778186c26465d3c4513372f8ffa4b8be4824b0b6110c03593bd680bc43ac4825f19190434c1dd63abdda0555f4 languageName: node linkType: hard -"@vaadin/overlay@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/overlay@npm:24.8.0" +"@vaadin/overlay@npm:~24.7.7": + version: 24.7.9 + resolution: "@vaadin/overlay@npm:24.7.9" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:^24.8.0" - "@vaadin/component-base": "npm:^24.8.0" - "@vaadin/vaadin-lumo-styles": "npm:^24.8.0" - "@vaadin/vaadin-material-styles": "npm:^24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" + "@vaadin/a11y-base": "npm:~24.7.9" + "@vaadin/component-base": "npm:~24.7.9" + "@vaadin/vaadin-lumo-styles": "npm:~24.7.9" + "@vaadin/vaadin-material-styles": "npm:~24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.9" lit: "npm:^3.0.0" - checksum: 10/d26330e761868a682013937c66977b5c6662251afa32df7fed0aea218977c25dc0f24a8d9b96561754b746943563c82c7a91e365cf50f8713a549562d63d9ec7 + checksum: 10/6790f954a39782f635312ad29edc939257cc4b3007908986ad4f04102cbc21348627aa5fc38ea63e261890fc1a77cd136e935df8ffda48dce65c090f7df4e438 languageName: node linkType: hard @@ -5152,37 +5152,46 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-lumo-styles@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/vaadin-lumo-styles@npm:24.8.0" +"@vaadin/vaadin-lumo-styles@npm:~24.7.7, @vaadin/vaadin-lumo-styles@npm:~24.7.9": + version: 24.7.9 + resolution: "@vaadin/vaadin-lumo-styles@npm:24.7.9" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:^24.8.0" - "@vaadin/icon": "npm:^24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" - checksum: 10/14d9c942ed88a9aa4579f0c8b8193d51affe3c0fdff781ecb9fb53aef6746d86317eb372526a15b0723017cd67b16bbb3ed252b01c89faa89bb0fca3d1b19855 + "@vaadin/component-base": "npm:~24.7.9" + "@vaadin/icon": "npm:~24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.9" + checksum: 10/a75ae75ca18fa4c4257f155e8632625b7379a4654c019e29ad5899fea0997275fb8ff519d0d37516e7d8f29466f449187b21f0d03cbd3d0e0a2b79abedf83e18 languageName: node linkType: hard -"@vaadin/vaadin-material-styles@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/vaadin-material-styles@npm:24.8.0" +"@vaadin/vaadin-material-styles@npm:~24.7.7, @vaadin/vaadin-material-styles@npm:~24.7.9": + version: 24.7.9 + resolution: "@vaadin/vaadin-material-styles@npm:24.7.9" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:^24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:^24.8.0" - checksum: 10/87917d5b20a1d77e7e38aff1e5be1d28a52a7c08777a01f44a6198cb4f0904ddf7257b23d622a02d098f38928ae3b168e2f2c81ac2c7b952e8056b4958154692 + "@vaadin/component-base": "npm:~24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.9" + checksum: 10/642bcd8ce3b696b34c80f35c4fdf95b79a34cc956c3eeb2de06335f6db31b07e80d956af3209e3874d1c0df04ecec20efc3358292b50e0aa495a5f94adf649cd languageName: node linkType: hard -"@vaadin/vaadin-themable-mixin@npm:24.8.0, @vaadin/vaadin-themable-mixin@npm:^24.8.0": - version: 24.8.0 - resolution: "@vaadin/vaadin-themable-mixin@npm:24.8.0" +"@vaadin/vaadin-themable-mixin@npm:24.7.7": + version: 24.7.7 + resolution: "@vaadin/vaadin-themable-mixin@npm:24.7.7" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" lit: "npm:^3.0.0" - style-observer: "npm:^0.0.8" - checksum: 10/141f8756e3330b9ee4efdd34e2febf832a70cf86802908d7fec589567bd4d3ce2820345cbe39fe5a6315bc6dee84065f781f48842ce864ef0a59589607ba1faa + checksum: 10/c4905df956baf4255029cf61f02366db61822e0db664baf279daf075e9122eb51bc85f0527f12f1bc004c279ab404c5b6f765a9d581ba729463165b6e2e07dd0 + languageName: node + linkType: hard + +"@vaadin/vaadin-themable-mixin@npm:~24.7.7, @vaadin/vaadin-themable-mixin@npm:~24.7.9": + version: 24.7.9 + resolution: "@vaadin/vaadin-themable-mixin@npm:24.7.9" + dependencies: + "@open-wc/dedupe-mixin": "npm:^1.3.0" + lit: "npm:^3.0.0" + checksum: 10/167827b3082b2fb1028f4ab036d6503667b20f51d81b5857f75390a0341d78067c13064073268de996b24383b4c7d732c621a617e57357003d32e76d1b464a0c languageName: node linkType: hard @@ -9338,8 +9347,8 @@ __metadata: "@types/tar": "npm:6.1.13" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@vaadin/combo-box": "npm:24.8.0" - "@vaadin/vaadin-themable-mixin": "npm:24.8.0" + "@vaadin/combo-box": "npm:24.7.7" + "@vaadin/vaadin-themable-mixin": "npm:24.7.7" "@vibrant/color": "npm:4.0.0" "@vitest/coverage-v8": "npm:3.2.4" "@vue/web-component-wrapper": "npm:1.3.0" @@ -13883,13 +13892,6 @@ __metadata: languageName: node linkType: hard -"style-observer@npm:^0.0.8": - version: 0.0.8 - resolution: "style-observer@npm:0.0.8" - checksum: 10/9c72ee12c61d48f64622a625ebff9bc4df009877e7ed9b26cec08e8159f6270f428aeea120f0e7c5567c8bbaa701846528fb5339dbdb930e84f2a66d382aeeb6 - languageName: node - linkType: hard - "superstruct@npm:2.0.2": version: 2.0.2 resolution: "superstruct@npm:2.0.2" From 68e22d23f1ecf7e1313e49ed82bc483e2a2817b2 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 26 Jun 2025 16:42:11 +0200 Subject: [PATCH 143/216] Fix filtering on device in entities config panel (#25948) * Fix filtering on device in entities config panel * fix * set filters from url twice to catch race... --- src/common/decorators/storage.ts | 5 +++-- src/components/ha-filter-blueprints.ts | 14 ++++++------ src/components/ha-filter-devices.ts | 15 ++++++++----- src/components/ha-filter-entities.ts | 14 +++++++----- src/components/ha-filter-floor-areas.ts | 17 ++++++-------- .../config/entities/ha-config-entities.ts | 22 +++++++------------ 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/src/common/decorators/storage.ts b/src/common/decorators/storage.ts index 03d4176548..80c258b83f 100644 --- a/src/common/decorators/storage.ts +++ b/src/common/decorators/storage.ts @@ -202,7 +202,6 @@ export function storage(options: { // Don't set the initial value if we have a value in localStorage if (this.__initialized || getValue() === undefined) { setValue(this, value); - this.requestUpdate(propertyKey, undefined); } }, configurable: true, @@ -212,11 +211,13 @@ export function storage(options: { const oldSetter = descriptor.set; newDescriptor = { ...descriptor, + get(this: ReactiveStorageElement) { + return getValue(); + }, set(this: ReactiveStorageElement, value) { // Don't set the initial value if we have a value in localStorage if (this.__initialized || getValue() === undefined) { setValue(this, value); - this.requestUpdate(propertyKey, undefined); } oldSetter?.call(this, value); }, diff --git a/src/components/ha-filter-blueprints.ts b/src/components/ha-filter-blueprints.ts index bdb2816ff3..c32d79659a 100644 --- a/src/components/ha-filter-blueprints.ts +++ b/src/components/ha-filter-blueprints.ts @@ -14,6 +14,7 @@ import "./ha-check-list-item"; import "./ha-expansion-panel"; import "./ha-icon-button"; import "./ha-list"; +import { deepEqual } from "../common/util/deep-equal"; @customElement("ha-filter-blueprints") export class HaFilterBlueprints extends LitElement { @@ -34,10 +35,11 @@ export class HaFilterBlueprints extends LitElement { public willUpdate(properties: PropertyValues) { super.willUpdate(properties); - if (!this.hasUpdated) { - if (this.value?.length) { - this._findRelated(); - } + if ( + properties.has("value") && + !deepEqual(this.value, properties.get("value")) + ) { + this._findRelated(); } } @@ -130,17 +132,15 @@ export class HaFilterBlueprints extends LitElement { } this.value = value; - - this._findRelated(); } private async _findRelated() { if (!this.value?.length) { + this.value = []; fireEvent(this, "data-table-filter-changed", { value: [], items: undefined, }); - this.value = []; return; } diff --git a/src/components/ha-filter-devices.ts b/src/components/ha-filter-devices.ts index 7beb3c05ea..17bf421295 100644 --- a/src/components/ha-filter-devices.ts +++ b/src/components/ha-filter-devices.ts @@ -6,6 +6,7 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; import { computeDeviceNameDisplay } from "../common/entity/compute_device_name"; import { stringCompare } from "../common/string/compare"; +import { deepEqual } from "../common/util/deep-equal"; import type { RelatedResult } from "../data/search"; import { findRelated } from "../data/search"; import { haStyleScrollbar } from "../resources/styles"; @@ -37,9 +38,13 @@ export class HaFilterDevices extends LitElement { if (!this.hasUpdated) { loadVirtualizer(); - if (this.value?.length) { - this._findRelated(); - } + } + + if ( + properties.has("value") && + !deepEqual(this.value, properties.get("value")) + ) { + this._findRelated(); } } @@ -110,7 +115,6 @@ export class HaFilterDevices extends LitElement { this.value = [...(this.value || []), value]; } listItem.selected = this.value?.includes(value); - this._findRelated(); } protected updated(changed) { @@ -160,11 +164,11 @@ export class HaFilterDevices extends LitElement { const relatedPromises: Promise[] = []; if (!this.value?.length) { + this.value = []; fireEvent(this, "data-table-filter-changed", { value: [], items: undefined, }); - this.value = []; return; } @@ -176,7 +180,6 @@ export class HaFilterDevices extends LitElement { relatedPromises.push(findRelated(this.hass, "device", deviceId)); } } - this.value = value; const results = await Promise.all(relatedPromises); const items = new Set(); for (const result of results) { diff --git a/src/components/ha-filter-entities.ts b/src/components/ha-filter-entities.ts index 3f078d813c..35da537f86 100644 --- a/src/components/ha-filter-entities.ts +++ b/src/components/ha-filter-entities.ts @@ -17,6 +17,7 @@ import "./ha-expansion-panel"; import "./ha-list"; import "./ha-state-icon"; import "./search-input-outlined"; +import { deepEqual } from "../common/util/deep-equal"; @customElement("ha-filter-entities") export class HaFilterEntities extends LitElement { @@ -39,9 +40,13 @@ export class HaFilterEntities extends LitElement { if (!this.hasUpdated) { loadVirtualizer(); - if (this.value?.length) { - this._findRelated(); - } + } + + if ( + properties.has("value") && + !deepEqual(this.value, properties.get("value")) + ) { + this._findRelated(); } } @@ -131,7 +136,6 @@ export class HaFilterEntities extends LitElement { this.value = [...(this.value || []), value]; } listItem.selected = this.value?.includes(value); - this._findRelated(); } private _expandedWillChange(ev) { @@ -178,11 +182,11 @@ export class HaFilterEntities extends LitElement { const relatedPromises: Promise[] = []; if (!this.value?.length) { + this.value = []; fireEvent(this, "data-table-filter-changed", { value: [], items: undefined, }); - this.value = []; return; } diff --git a/src/components/ha-filter-floor-areas.ts b/src/components/ha-filter-floor-areas.ts index 5f4e811b4f..38ee414683 100644 --- a/src/components/ha-filter-floor-areas.ts +++ b/src/components/ha-filter-floor-areas.ts @@ -20,6 +20,7 @@ import "./ha-icon-button"; import "./ha-list"; import "./ha-svg-icon"; import "./ha-tree-indicator"; +import { deepEqual } from "../common/util/deep-equal"; @customElement("ha-filter-floor-areas") export class HaFilterFloorAreas extends LitElement { @@ -41,10 +42,11 @@ export class HaFilterFloorAreas extends LitElement { public willUpdate(properties: PropertyValues) { super.willUpdate(properties); - if (!this.hasUpdated) { - if (this.value?.floors?.length || this.value?.areas?.length) { - this._findRelated(); - } + if ( + properties.has("value") && + !deepEqual(this.value, properties.get("value")) + ) { + this._findRelated(); } } @@ -174,8 +176,6 @@ export class HaFilterFloorAreas extends LitElement { } listItem.selected = this.value[type]?.includes(value); - - this._findRelated(); } protected updated(changed) { @@ -188,10 +188,6 @@ export class HaFilterFloorAreas extends LitElement { } } - protected firstUpdated() { - this._findRelated(); - } - private _expandedWillChange(ev) { this._shouldRender = ev.detail.expanded; } @@ -226,6 +222,7 @@ export class HaFilterFloorAreas extends LitElement { !this.value || (!this.value.areas?.length && !this.value.floors?.length) ) { + this.value = {}; fireEvent(this, "data-table-filter-changed", { value: {}, items: undefined, diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index e1607485cb..106a6fb8f3 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -1099,10 +1099,10 @@ ${ } protected firstUpdated() { + this._setFiltersFromUrl(); fetchEntitySourcesWithCache(this.hass).then((sources) => { this._entitySources = sources; }); - this._setFiltersFromUrl(); if (Object.keys(this._filters).length) { return; } @@ -1116,7 +1116,7 @@ ${ const configEntry = this._searchParms.get("config_entry"); const subEntry = this._searchParms.get("sub_entry"); const device = this._searchParms.get("device"); - const label = this._searchParms.has("label"); + const label = this._searchParms.get("label"); if (!domain && !configEntry && !label && !device) { return; @@ -1128,21 +1128,10 @@ ${ "ha-filter-states": [], "ha-filter-integrations": domain ? [domain] : [], "ha-filter-devices": device ? [device] : [], + "ha-filter-labels": label ? [label] : [], config_entry: configEntry ? [configEntry] : [], sub_entry: subEntry ? [subEntry] : [], }; - this._filterLabel(); - } - - private _filterLabel() { - const label = this._searchParms.get("label"); - if (!label) { - return; - } - this._filters = { - ...this._filters, - "ha-filter-labels": [label], - }; } private _clearFilter() { @@ -1152,6 +1141,11 @@ ${ public willUpdate(changedProps: PropertyValues): void { super.willUpdate(changedProps); + + if (!this.hasUpdated) { + this._setFiltersFromUrl(); + } + const oldHass = changedProps.get("hass"); let changed = false; if (!this.hass || !this._entities) { From 369881f8a6b270989880a11e86b10d4c4b86c41c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 26 Jun 2025 19:21:15 +0200 Subject: [PATCH 144/216] Fix expand icon for entries and sub entries (#25955) --- .../integrations/ha-config-entry-row.ts | 25 +++++++++++-------- .../integrations/ha-config-sub-entry-row.ts | 10 +++++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/panels/config/integrations/ha-config-entry-row.ts b/src/panels/config/integrations/ha-config-entry-row.ts index afee3d8efb..41e843c0c4 100644 --- a/src/panels/config/integrations/ha-config-entry-row.ts +++ b/src/panels/config/integrations/ha-config-entry-row.ts @@ -1,7 +1,6 @@ import { mdiAlertCircle, mdiChevronDown, - mdiChevronUp, mdiCogOutline, mdiDelete, mdiDevices, @@ -58,6 +57,7 @@ import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entr import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow"; import { showSubConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-sub-config-flow"; +import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { fileDownload } from "../../../util/file_download"; @@ -69,7 +69,6 @@ import { import "./ha-config-entry-device-row"; import { renderConfigEntryError } from "./ha-config-integration-page"; import "./ha-config-sub-entry-row"; -import { haStyle } from "../../../resources/styles"; @customElement("ha-config-entry-row") class HaConfigEntryRow extends LitElement { @@ -178,8 +177,8 @@ class HaConfigEntryRow extends LitElement { > ${subEntries.length || ownDevices.length ? html`` @@ -410,15 +409,15 @@ class HaConfigEntryRow extends LitElement { @@ -742,6 +741,10 @@ class HaConfigEntryRow extends LitElement { css` .expand-button { margin: 0 -12px; + transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1); + } + .expand-button.expanded { + transform: rotate(180deg); } ha-md-list { border: 1px solid var(--divider-color); diff --git a/src/panels/config/integrations/ha-config-sub-entry-row.ts b/src/panels/config/integrations/ha-config-sub-entry-row.ts index 29d832ca5f..ae4311fd69 100644 --- a/src/panels/config/integrations/ha-config-sub-entry-row.ts +++ b/src/panels/config/integrations/ha-config-sub-entry-row.ts @@ -1,6 +1,5 @@ import { mdiChevronDown, - mdiChevronUp, mdiCogOutline, mdiDelete, mdiDevices, @@ -10,6 +9,7 @@ import { } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; import type { ConfigEntry, SubEntry } from "../../../data/config_entries"; import { deleteSubEntry } from "../../../data/config_entries"; import type { DeviceRegistryEntry } from "../../../data/device_registry"; @@ -56,8 +56,8 @@ class HaConfigSubEntryRow extends LitElement { > ${devices.length || services.length ? html`` @@ -239,6 +239,10 @@ class HaConfigSubEntryRow extends LitElement { static styles = css` .expand-button { margin: 0 -12px; + transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1); + } + .expand-button.expanded { + transform: rotate(180deg); } ha-md-list { border: 1px solid var(--divider-color); From ef964a2717c735609355c1e79b3c72d45ff9418c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:32:39 +0200 Subject: [PATCH 145/216] Update dependency typescript-eslint to v8.35.0 (#25956) --- package.json | 2 +- yarn.lock | 142 +++++++++++++++++++++++++-------------------------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index af35e02e77..aa47dd1da7 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,7 @@ "terser-webpack-plugin": "5.3.14", "ts-lit-plugin": "2.0.2", "typescript": "5.8.3", - "typescript-eslint": "8.34.1", + "typescript-eslint": "8.35.0", "vite-tsconfig-paths": "5.1.4", "vitest": "3.2.4", "webpack-stats-plugin": "1.1.3", diff --git a/yarn.lock b/yarn.lock index 4dca964822..a5c9184b9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4881,105 +4881,105 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/eslint-plugin@npm:8.34.1" +"@typescript-eslint/eslint-plugin@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.35.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.34.1" - "@typescript-eslint/type-utils": "npm:8.34.1" - "@typescript-eslint/utils": "npm:8.34.1" - "@typescript-eslint/visitor-keys": "npm:8.34.1" + "@typescript-eslint/scope-manager": "npm:8.35.0" + "@typescript-eslint/type-utils": "npm:8.35.0" + "@typescript-eslint/utils": "npm:8.35.0" + "@typescript-eslint/visitor-keys": "npm:8.35.0" graphemer: "npm:^1.4.0" ignore: "npm:^7.0.0" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.1.0" peerDependencies: - "@typescript-eslint/parser": ^8.34.1 + "@typescript-eslint/parser": ^8.35.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/a4b1cffcb5f2b4f5f4c267cd4519d0e2df73c8017c93200d5a86df7882073f18cf4f5d0604aa8dafb6e4dc4ab391ae8e9a2161631fb1eca9bca32af063acdaf2 + checksum: 10/a87ef0ed958bfb1d2fd38f41d1628d5411136c201e1361ba2ea136a3e9ec01516abf3d4e963eee1bba60132a630814a8af06f4cc014fafd578d8b45f62771eb0 languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/parser@npm:8.34.1" +"@typescript-eslint/parser@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/parser@npm:8.35.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.34.1" - "@typescript-eslint/types": "npm:8.34.1" - "@typescript-eslint/typescript-estree": "npm:8.34.1" - "@typescript-eslint/visitor-keys": "npm:8.34.1" + "@typescript-eslint/scope-manager": "npm:8.35.0" + "@typescript-eslint/types": "npm:8.35.0" + "@typescript-eslint/typescript-estree": "npm:8.35.0" + "@typescript-eslint/visitor-keys": "npm:8.35.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/c862baa6f5260bf4b63d79ae4d68fc09b7e094ea9f28ee461887cbb660ef1339e829119029e1e6ba40335fc9e85d134a04036965bc261f7abf4d0e605cb485ec + checksum: 10/bd0e406938a4b305bb409611c29bcdc01e3a63c1c47fcbf527a7e2033ae71f6de7c7f95aa4f8973dcd38f5ec26f8e2857e01f160ebc538453161735c17b644da languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/project-service@npm:8.34.1" +"@typescript-eslint/project-service@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/project-service@npm:8.35.0" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.34.1" - "@typescript-eslint/types": "npm:^8.34.1" + "@typescript-eslint/tsconfig-utils": "npm:^8.35.0" + "@typescript-eslint/types": "npm:^8.35.0" debug: "npm:^4.3.4" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/5dad268397cd2d601e5e65ab9628c59c6687a79cac31e0d0eac2b434505639fd8767f1a2d5b077b158c581f5a48bb96e7da361560fb26b70b680272e39c6f976 + checksum: 10/a9419da92231aa27f75078fcffab1d02398b50fdb7d5399775a414ba02570682b4b60cdfafb544a021b0dc2372f029c4195f5ae17c50deb11c25661b2ac18a74 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/scope-manager@npm:8.34.1" +"@typescript-eslint/scope-manager@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/scope-manager@npm:8.35.0" dependencies: - "@typescript-eslint/types": "npm:8.34.1" - "@typescript-eslint/visitor-keys": "npm:8.34.1" - checksum: 10/cd3f2ba811e4794c78d7f9df0ff1ad6ce33d162d87986e67c4ec409963f07384bd184dbddc613e89a5cc753a47469e7fa9d02c507b57aa9e0fdc8a97c0378353 + "@typescript-eslint/types": "npm:8.35.0" + "@typescript-eslint/visitor-keys": "npm:8.35.0" + checksum: 10/36082fe476cf744c016a554e5ce77e6beb7d4d9992b513382bdf7e8f7d044ffd780fefc3f698e53780ead677d0afaf93e82bade10f08933e2757750bfd273d13 languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.34.1, @typescript-eslint/tsconfig-utils@npm:^8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.1" +"@typescript-eslint/tsconfig-utils@npm:8.35.0, @typescript-eslint/tsconfig-utils@npm:^8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.35.0" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/81a874a433c4e91ee2509d4eda43932b8348e9404da2d11e621bf3b8bec26a6ab84bd3870215dcb09df950182e2b5e2539be30fc262c30edff0e42ca5d707465 + checksum: 10/4160928313ccbe8b169a009b9c1220826c7df7aab427f960c31f3b838931bc7a121ebee8040118481e4528e2e3cf1b26da047c6ac1d802ecff2ef7206026ea6b languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/type-utils@npm:8.34.1" +"@typescript-eslint/type-utils@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/type-utils@npm:8.35.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.34.1" - "@typescript-eslint/utils": "npm:8.34.1" + "@typescript-eslint/typescript-estree": "npm:8.35.0" + "@typescript-eslint/utils": "npm:8.35.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.1.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/1c8153a5b1cf488b6d1642d752caba8631f183f17031660859355342d1139e4dea9e0dd9c97d6bad644a91ee26461ddd1993303d0542e6f1b7850af1ca71e96e + checksum: 10/796210b0f099a9382ab8a437556e2710fa481f5288e098d7c45e3c26827df762968241d8ac9542d805274e3755785b3afd1ad2018c79efce72a33515f2e5613d languageName: node linkType: hard -"@typescript-eslint/types@npm:8.34.1, @typescript-eslint/types@npm:^8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/types@npm:8.34.1" - checksum: 10/09cb344af38e1e0f8e60968ff6038e8b27a453dea22be433b531e2b50b45448b8646f11249279d47e153f0a5299f8f621a84e81db8bcf5421bd90c94caae6416 +"@typescript-eslint/types@npm:8.35.0, @typescript-eslint/types@npm:^8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/types@npm:8.35.0" + checksum: 10/34b5e6da2c59ea84cd528608fff0cc14b102fd23f5517dfee4ef38c9372861d80b5bf92445c9679674f0a4f8dc4ded5066c1bca2bc5569c47515f94568984f35 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/typescript-estree@npm:8.34.1" +"@typescript-eslint/typescript-estree@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.35.0" dependencies: - "@typescript-eslint/project-service": "npm:8.34.1" - "@typescript-eslint/tsconfig-utils": "npm:8.34.1" - "@typescript-eslint/types": "npm:8.34.1" - "@typescript-eslint/visitor-keys": "npm:8.34.1" + "@typescript-eslint/project-service": "npm:8.35.0" + "@typescript-eslint/tsconfig-utils": "npm:8.35.0" + "@typescript-eslint/types": "npm:8.35.0" + "@typescript-eslint/visitor-keys": "npm:8.35.0" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -4988,32 +4988,32 @@ __metadata: ts-api-utils: "npm:^2.1.0" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/40ffa31d8005115fb8efe47eeea484ad8a32a55e8bc2e27e4ad7b89b9fb1b962254c4c4ec9c00b4a5d52c5fa45b25b69ef62a98135f478e486f51ea5ba0ad4e9 + checksum: 10/4dff7c5a8853c8f4e30d35565c62d3ad5bf8445309bd465d94e9bca725853012bb9f58896a04207c30e10b6669511caac8c0f080ed781c93a3db81d5808195aa languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/utils@npm:8.34.1" +"@typescript-eslint/utils@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/utils@npm:8.35.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.34.1" - "@typescript-eslint/types": "npm:8.34.1" - "@typescript-eslint/typescript-estree": "npm:8.34.1" + "@typescript-eslint/scope-manager": "npm:8.35.0" + "@typescript-eslint/types": "npm:8.35.0" + "@typescript-eslint/typescript-estree": "npm:8.35.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/7e14ef16222d48aa668c2b436b7eec893e8baf05a18c4bcdf353fa6ce4b5526db3d3945be5a7bd4dab0202805f205c4a904cf8646fa157f53b761c090d9c5e7b + checksum: 10/24b4af650a8f4d21515498c1c38624717f210d68aedc6cee6958f4e8c36504d871176800020764500f64e078dda1ce23c19bbe19f8f5f7efbe995eb1afca42f2 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.34.1": - version: 8.34.1 - resolution: "@typescript-eslint/visitor-keys@npm:8.34.1" +"@typescript-eslint/visitor-keys@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.35.0" dependencies: - "@typescript-eslint/types": "npm:8.34.1" + "@typescript-eslint/types": "npm:8.35.0" eslint-visitor-keys: "npm:^4.2.1" - checksum: 10/6fbaa838dc040c6ff6d4472b9a1480f1407eb591924fb4d371fe0224dafcb40ac5476b733fea33ad0898c3174430918b0456c5209b5b7e176cb04c0c9daacbd8 + checksum: 10/c0acb13aac3a2be5e82844f7d2e86137347efdd04661dbf9fa69ef04a19dd2f1eb2f1eb6bfbfbaada78a46884308d2c0e0b5d0d1a094c84f2dfb670b67ac2b3b languageName: node linkType: hard @@ -9432,7 +9432,7 @@ __metadata: tinykeys: "npm:3.0.0" ts-lit-plugin: "npm:2.0.2" typescript: "npm:5.8.3" - typescript-eslint: "npm:8.34.1" + typescript-eslint: "npm:8.35.0" ua-parser-js: "npm:2.0.3" vis-data: "npm:7.1.9" vite-tsconfig-paths: "npm:5.1.4" @@ -14464,17 +14464,17 @@ __metadata: languageName: node linkType: hard -"typescript-eslint@npm:8.34.1": - version: 8.34.1 - resolution: "typescript-eslint@npm:8.34.1" +"typescript-eslint@npm:8.35.0": + version: 8.35.0 + resolution: "typescript-eslint@npm:8.35.0" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.34.1" - "@typescript-eslint/parser": "npm:8.34.1" - "@typescript-eslint/utils": "npm:8.34.1" + "@typescript-eslint/eslint-plugin": "npm:8.35.0" + "@typescript-eslint/parser": "npm:8.35.0" + "@typescript-eslint/utils": "npm:8.35.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/78088abe01b7f6ba4c6036a43eb3992dfe16dc7604db73e0b9f3c7c4adb452ab715c4d644344ef89ee52c941f7536a290b22a09b0e35dcef2cf158c99b49b17d + checksum: 10/350f584dfe2de1863954d65e27f8a02849569970ee4e0c108d881180d044ffeb4cc112a447ea835f347357219219afefed35084bd0bfd27496eb882f221eb5de languageName: node linkType: hard From e8cb85f7fffa43abaf41fcf67d28aa93841fa812 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 26 Jun 2025 23:17:30 +0200 Subject: [PATCH 146/216] Disable fullscreen editor for editors that are already fullscreen (#25959) * Disabled fullscreen editor for editors that are already fullscreen * Update ha-code-editor.ts --- src/components/ha-code-editor.ts | 38 +++++++++---------- src/components/ha-yaml-editor.ts | 4 ++ .../config/automation/ha-automation-editor.ts | 1 + src/panels/config/scene/ha-scene-editor.ts | 1 + src/panels/config/script/ha-script-editor.ts | 1 + src/panels/lovelace/hui-editor.ts | 1 + 6 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index be825c2b42..be3eb9d283 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -61,8 +61,8 @@ export class HaCodeEditor extends ReactiveElement { @property({ type: Boolean }) public error = false; - @property({ type: Boolean, attribute: "enable-fullscreen" }) - public enableFullscreen = true; + @property({ type: Boolean, attribute: "disable-fullscreen" }) + public disableFullscreen = false; @state() private _value = ""; @@ -179,7 +179,7 @@ export class HaCodeEditor extends ReactiveElement { if (changedProps.has("_isFullscreen")) { this.classList.toggle("fullscreen", this._isFullscreen); } - if (changedProps.has("enableFullscreen")) { + if (changedProps.has("disableFullscreen")) { this._updateFullscreenButton(); } } @@ -263,7 +263,7 @@ export class HaCodeEditor extends ReactiveElement { private _updateFullscreenButton() { const existingButton = this.renderRoot.querySelector(".fullscreen-button"); - if (!this.enableFullscreen) { + if (this.disableFullscreen) { // Remove button if it exists and fullscreen is disabled if (existingButton) { existingButton.remove(); @@ -317,7 +317,7 @@ export class HaCodeEditor extends ReactiveElement { e.preventDefault(); e.stopPropagation(); this._toggleFullscreen(); - } else if (e.key === "F11" && this.enableFullscreen) { + } else if (e.key === "F11" && !this.disableFullscreen) { e.preventDefault(); e.stopPropagation(); this._toggleFullscreen(); @@ -557,7 +557,7 @@ export class HaCodeEditor extends ReactiveElement { position: absolute; top: 8px; right: 8px; - z-index: 10; + z-index: 1; color: var(--secondary-text-color); background-color: var(--card-background-color); border-radius: 50%; @@ -589,17 +589,17 @@ export class HaCodeEditor extends ReactiveElement { right: 0 !important; bottom: 0 !important; z-index: 9999 !important; - background-color: var(--primary-background-color) !important; + background-color: var( + --code-editor-background-color, + var(--mdc-text-field-fill-color, whitesmoke) + ) !important; margin: 0 !important; - padding: 16px !important; - /* Respect iOS safe areas while accounting for header */ - padding-top: max(16px, env(safe-area-inset-top)) !important; - padding-left: max(16px, env(safe-area-inset-left)) !important; - padding-right: max(16px, env(safe-area-inset-right)) !important; - padding-bottom: max(16px, env(safe-area-inset-bottom)) !important; + padding-top: var(--safe-area-inset-top) !important; + padding-left: var(--safe-area-inset-left) !important; + padding-right: var(--safe-area-inset-right) !important; + padding-bottom: var(--safe-area-inset-bottom) !important; box-sizing: border-box !important; - display: flex !important; - flex-direction: column !important; + display: block !important; } :host(.fullscreen) .cm-editor { @@ -609,12 +609,8 @@ export class HaCodeEditor extends ReactiveElement { } :host(.fullscreen) .fullscreen-button { - position: fixed; - top: calc( - var(--header-height, 56px) + max(8px, env(safe-area-inset-top)) - ); - right: max(24px, calc(env(safe-area-inset-right) + 8px)); - z-index: 10000; + top: calc(var(--safe-area-inset-top, 0px) + 8px); + right: calc(var(--safe-area-inset-right, 0px) + 8px); } `; } diff --git a/src/components/ha-yaml-editor.ts b/src/components/ha-yaml-editor.ts index c2a95a4022..53a7e579c7 100644 --- a/src/components/ha-yaml-editor.ts +++ b/src/components/ha-yaml-editor.ts @@ -44,6 +44,9 @@ export class HaYamlEditor extends LitElement { @property({ attribute: "read-only", type: Boolean }) public readOnly = false; + @property({ type: Boolean, attribute: "disable-fullscreen" }) + public disableFullscreen = false; + @property({ type: Boolean }) public required = false; @property({ attribute: "copy-clipboard", type: Boolean }) @@ -110,6 +113,7 @@ export class HaYamlEditor extends LitElement { .hass=${this.hass} .value=${this._yaml} .readOnly=${this.readOnly} + .disableFullscreen=${this.disableFullscreen} mode="yaml" autocomplete-entities autocomplete-icons diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 09913aec3c..691fbb4e82 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -502,6 +502,7 @@ export class HaAutomationEditor extends PreventUnsavedMixin( .readOnly=${this._readOnly} @value-changed=${this._yamlChanged} .showErrors=${false} + disable-fullscreen >` : nothing}
    diff --git a/src/panels/config/scene/ha-scene-editor.ts b/src/panels/config/scene/ha-scene-editor.ts index 66494fbf3e..f0d66cef4f 100644 --- a/src/panels/config/scene/ha-scene-editor.ts +++ b/src/panels/config/scene/ha-scene-editor.ts @@ -321,6 +321,7 @@ export class HaSceneEditor extends PreventUnsavedMixin( .defaultValue=${this._config} @value-changed=${this._yamlChanged} .showErrors=${false} + disable-fullscreen >`; } diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index 4eaffe3d45..af68900fde 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -440,6 +440,7 @@ export class HaScriptEditor extends SubscribeMixin( .hass=${this.hass} .defaultValue=${this._preprocessYaml()} .readOnly=${this._readOnly} + disable-fullscreen @value-changed=${this._yamlChanged} .showErrors=${false} >` diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index a7fd0c0216..1a08e2c8a4 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -89,6 +89,7 @@ class LovelaceFullConfigEditor extends LitElement { .hass=${this.hass} @value-changed=${this._yamlChanged} @editor-save=${this._handleSave} + disable-fullscreen dir="ltr" > From 9ec38c7dd90f2a067c754ee04fcb1a67667b16e1 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 26 Jun 2025 23:22:06 +0200 Subject: [PATCH 147/216] Bump vaadin to 24.7.9 (#25963) --- package.json | 4 ++-- yarn.lock | 62 ++++++++++++++++++++++------------------------------ 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index aa47dd1da7..9259cb5d5c 100644 --- a/package.json +++ b/package.json @@ -89,8 +89,8 @@ "@thomasloven/round-slider": "0.6.0", "@tsparticles/engine": "3.8.1", "@tsparticles/preset-links": "3.2.0", - "@vaadin/combo-box": "24.7.7", - "@vaadin/vaadin-themable-mixin": "24.7.7", + "@vaadin/combo-box": "24.7.9", + "@vaadin/vaadin-themable-mixin": "24.7.9", "@vibrant/color": "4.0.0", "@vue/web-component-wrapper": "1.3.0", "@webcomponents/scoped-custom-element-registry": "0.0.10", diff --git a/yarn.lock b/yarn.lock index a5c9184b9e..0c061641ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5017,7 +5017,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/a11y-base@npm:~24.7.7, @vaadin/a11y-base@npm:~24.7.9": +"@vaadin/a11y-base@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/a11y-base@npm:24.7.9" dependencies: @@ -5029,28 +5029,28 @@ __metadata: languageName: node linkType: hard -"@vaadin/combo-box@npm:24.7.7": - version: 24.7.7 - resolution: "@vaadin/combo-box@npm:24.7.7" +"@vaadin/combo-box@npm:24.7.9": + version: 24.7.9 + resolution: "@vaadin/combo-box@npm:24.7.9" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.7.7" - "@vaadin/component-base": "npm:~24.7.7" - "@vaadin/field-base": "npm:~24.7.7" - "@vaadin/input-container": "npm:~24.7.7" - "@vaadin/item": "npm:~24.7.7" - "@vaadin/lit-renderer": "npm:~24.7.7" - "@vaadin/overlay": "npm:~24.7.7" - "@vaadin/vaadin-lumo-styles": "npm:~24.7.7" - "@vaadin/vaadin-material-styles": "npm:~24.7.7" - "@vaadin/vaadin-themable-mixin": "npm:~24.7.7" + "@vaadin/a11y-base": "npm:~24.7.9" + "@vaadin/component-base": "npm:~24.7.9" + "@vaadin/field-base": "npm:~24.7.9" + "@vaadin/input-container": "npm:~24.7.9" + "@vaadin/item": "npm:~24.7.9" + "@vaadin/lit-renderer": "npm:~24.7.9" + "@vaadin/overlay": "npm:~24.7.9" + "@vaadin/vaadin-lumo-styles": "npm:~24.7.9" + "@vaadin/vaadin-material-styles": "npm:~24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:~24.7.9" lit: "npm:^3.0.0" - checksum: 10/ac9af96785d03ede12ac85c6a00f94a2122fc85db168ca2669a716138bc99a7df29630d4ede3274a6a9e97b53eac074c2f86480be0c3468d1b0118a4aca2aecd + checksum: 10/cd63ecbb0b8b260907aa5853faac8846f1d8c0d5ace1a7621b896109eb254afe1d68ff8b41d776bb253d04655e01c2905a4c361f1ad917de9dbde30c8cf9a5fd languageName: node linkType: hard -"@vaadin/component-base@npm:~24.7.7, @vaadin/component-base@npm:~24.7.9": +"@vaadin/component-base@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/component-base@npm:24.7.9" dependencies: @@ -5063,7 +5063,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/field-base@npm:~24.7.7": +"@vaadin/field-base@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/field-base@npm:24.7.9" dependencies: @@ -5090,7 +5090,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/input-container@npm:~24.7.7": +"@vaadin/input-container@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/input-container@npm:24.7.9" dependencies: @@ -5104,7 +5104,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/item@npm:~24.7.7": +"@vaadin/item@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/item@npm:24.7.9" dependencies: @@ -5120,7 +5120,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/lit-renderer@npm:~24.7.7": +"@vaadin/lit-renderer@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/lit-renderer@npm:24.7.9" dependencies: @@ -5129,7 +5129,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/overlay@npm:~24.7.7": +"@vaadin/overlay@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/overlay@npm:24.7.9" dependencies: @@ -5152,7 +5152,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-lumo-styles@npm:~24.7.7, @vaadin/vaadin-lumo-styles@npm:~24.7.9": +"@vaadin/vaadin-lumo-styles@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/vaadin-lumo-styles@npm:24.7.9" dependencies: @@ -5164,7 +5164,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-material-styles@npm:~24.7.7, @vaadin/vaadin-material-styles@npm:~24.7.9": +"@vaadin/vaadin-material-styles@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/vaadin-material-styles@npm:24.7.9" dependencies: @@ -5175,17 +5175,7 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-themable-mixin@npm:24.7.7": - version: 24.7.7 - resolution: "@vaadin/vaadin-themable-mixin@npm:24.7.7" - dependencies: - "@open-wc/dedupe-mixin": "npm:^1.3.0" - lit: "npm:^3.0.0" - checksum: 10/c4905df956baf4255029cf61f02366db61822e0db664baf279daf075e9122eb51bc85f0527f12f1bc004c279ab404c5b6f765a9d581ba729463165b6e2e07dd0 - languageName: node - linkType: hard - -"@vaadin/vaadin-themable-mixin@npm:~24.7.7, @vaadin/vaadin-themable-mixin@npm:~24.7.9": +"@vaadin/vaadin-themable-mixin@npm:24.7.9, @vaadin/vaadin-themable-mixin@npm:~24.7.9": version: 24.7.9 resolution: "@vaadin/vaadin-themable-mixin@npm:24.7.9" dependencies: @@ -9347,8 +9337,8 @@ __metadata: "@types/tar": "npm:6.1.13" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@vaadin/combo-box": "npm:24.7.7" - "@vaadin/vaadin-themable-mixin": "npm:24.7.7" + "@vaadin/combo-box": "npm:24.7.9" + "@vaadin/vaadin-themable-mixin": "npm:24.7.9" "@vibrant/color": "npm:4.0.0" "@vitest/coverage-v8": "npm:3.2.4" "@vue/web-component-wrapper": "npm:1.3.0" From 7693a4dc245ecd269a5e097c504dd26358f3686c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 27 Jun 2025 10:40:15 +0200 Subject: [PATCH 148/216] Use areas dashboard name in the top bar (#25969) --- src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts b/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts index 3b882837bf..9ec7bc9486 100644 --- a/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts @@ -78,7 +78,6 @@ export class AreasDashboardStrategy extends ReactiveElement { return { views: [ { - title: "Home", icon: "mdi:home", path: "home", strategy: { From 33d5cecc8541bf8b454c324aee3d66b5836d7ffc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 15:12:14 +0300 Subject: [PATCH 149/216] Update dependency ua-parser-js to v2.0.4 (#25968) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9259cb5d5c..de25c358cf 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "stacktrace-js": "2.0.2", "superstruct": "2.0.2", "tinykeys": "3.0.0", - "ua-parser-js": "2.0.3", + "ua-parser-js": "2.0.4", "vis-data": "7.1.9", "vue": "2.7.16", "vue2-daterange-picker": "0.6.8", diff --git a/yarn.lock b/yarn.lock index 0c061641ae..d2b5c19bf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9423,7 +9423,7 @@ __metadata: ts-lit-plugin: "npm:2.0.2" typescript: "npm:5.8.3" typescript-eslint: "npm:8.35.0" - ua-parser-js: "npm:2.0.3" + ua-parser-js: "npm:2.0.4" vis-data: "npm:7.1.9" vite-tsconfig-paths: "npm:5.1.4" vitest: "npm:3.2.4" @@ -14515,9 +14515,9 @@ __metadata: languageName: node linkType: hard -"ua-parser-js@npm:2.0.3": - version: 2.0.3 - resolution: "ua-parser-js@npm:2.0.3" +"ua-parser-js@npm:2.0.4": + version: 2.0.4 + resolution: "ua-parser-js@npm:2.0.4" dependencies: "@types/node-fetch": "npm:^2.6.12" detect-europe-js: "npm:^0.1.2" @@ -14526,7 +14526,7 @@ __metadata: ua-is-frozen: "npm:^0.1.2" bin: ua-parser-js: script/cli.js - checksum: 10/f181f1c976d67a2a8cdd396f8c5cecc8bfe4ada7183e7ef06a81a09aa9dc98480048380240f6374189cf2ab7e6275b711f9732e26b7ed2a14f3e4fe3576b8842 + checksum: 10/eb3a57cd4aea6c42d2d766761ccf38cdc4576075646dec611efc336f0d1e640896ec4ca084142a1fedbf25c589e093e2cad50c49a22d089e234029ecb9b8d2e4 languageName: node linkType: hard From 8aabb1f32fc14ad47902b2bb64381afa8186deeb Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Fri, 27 Jun 2025 15:57:52 +0200 Subject: [PATCH 150/216] Dev Tools: Remove excessive space from "Input date times" (#25973) Remove excessive space from "input date times" --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 6c5bc5becf..a9ad96ce5e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -8665,7 +8665,7 @@ "input_button": "Input buttons", "input_text": "Input texts", "input_number": "Input numbers", - "input_datetime": "Input date times", + "input_datetime": "Input datetimes", "input_select": "Input selects", "template": "Template entities", "universal": "Universal media player entities", From 22c798c9d6cf91cbab3b09d356b56fb3eb2c6474 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 27 Jun 2025 15:59:14 +0200 Subject: [PATCH 151/216] Add Claude to gitignore (#25966) --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 7de50364e7..a6fef79fcb 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,7 @@ src/cast/dev_const.ts # test coverage test/coverage/ + +# AI tooling +.claude + From 9d8a5b366ecf49c8e649a44ab46bf00f84cac76e Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Fri, 27 Jun 2025 17:41:58 +0200 Subject: [PATCH 152/216] Use entity format state if only one entity for that domain in the area card (#25964) Use entity format state if only one entity is area card --- src/panels/lovelace/cards/hui-area-card.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index 1f545e3a78..68dce2164e 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -348,6 +348,14 @@ export class HuiAreaCard extends LitElement implements LovelaceCard { return undefined; } + // If only one entity, return its formatted state + if (entities.length === 1) { + const stateObj = entities[0]; + return isUnavailableState(stateObj.state) + ? "" + : this.hass.formatEntityState(stateObj); + } + // Use the first entity's unit_of_measurement for formatting const uom = entities.find( (entity) => entity.attributes.unit_of_measurement From 6690d1ef22fb22e17e772163bf18513e69e3757c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 17:54:52 +0200 Subject: [PATCH 153/216] Update dependency @types/leaflet to v1.9.19 (#25974) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index de25c358cf..177d508d25 100644 --- a/package.json +++ b/package.json @@ -168,7 +168,7 @@ "@types/glob": "8.1.0", "@types/html-minifier-terser": "7.0.2", "@types/js-yaml": "4.0.9", - "@types/leaflet": "1.9.18", + "@types/leaflet": "1.9.19", "@types/leaflet-draw": "1.0.12", "@types/leaflet.markercluster": "1.5.5", "@types/lodash.merge": "4.6.9", diff --git a/yarn.lock b/yarn.lock index d2b5c19bf5..ef5f2b71fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4613,12 +4613,12 @@ __metadata: languageName: node linkType: hard -"@types/leaflet@npm:*, @types/leaflet@npm:1.9.18": - version: 1.9.18 - resolution: "@types/leaflet@npm:1.9.18" +"@types/leaflet@npm:*, @types/leaflet@npm:1.9.19": + version: 1.9.19 + resolution: "@types/leaflet@npm:1.9.19" dependencies: "@types/geojson": "npm:*" - checksum: 10/33085a5377e3c5656b9c2d5e17e2a7530363fbb1bb5825d2e959945c33d9f4579efca8eed9299cbe60609d19be2debbb8b14782197ac5466a86fb83b903a5e8d + checksum: 10/5e6b2c15d7b77c760a44ed2b66e317b94800d4a0d23ad6073f80d936cd40c8162c2e4df95a06835b58f122fcbee7684fc618f98ba40b32a484ed3e413f356dbb languageName: node linkType: hard @@ -9326,7 +9326,7 @@ __metadata: "@types/glob": "npm:8.1.0" "@types/html-minifier-terser": "npm:7.0.2" "@types/js-yaml": "npm:4.0.9" - "@types/leaflet": "npm:1.9.18" + "@types/leaflet": "npm:1.9.19" "@types/leaflet-draw": "npm:1.0.12" "@types/leaflet.markercluster": "npm:1.5.5" "@types/lodash.merge": "npm:4.6.9" From f08877437e1a056c561d2dd15f78fd63efd988ff Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 27 Jun 2025 18:06:23 +0200 Subject: [PATCH 154/216] Add initial instructions file for GitHub Copilot and Claude Code (#25967) --- .github/copilot-instructions.md | 592 ++++++++++++++++++++++++++++++++ CLAUDE.md | 1 + 2 files changed, 593 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 120000 CLAUDE.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..d52ff00f5e --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,592 @@ +# GitHub Copilot & Claude Code Instructions + +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. + +## Table of Contents + +- [Quick Reference](#quick-reference) +- [Core Architecture](#core-architecture) +- [Development Standards](#development-standards) +- [Component Library](#component-library) +- [Common Patterns](#common-patterns) +- [Text and Copy Guidelines](#text-and-copy-guidelines) +- [Development Workflow](#development-workflow) +- [Review Guidelines](#review-guidelines) + +## Quick Reference + +### Essential Commands + +```bash +yarn lint # ESLint + Prettier + TypeScript + Lit +yarn format # Auto-fix ESLint + Prettier +yarn lint:types # TypeScript compiler +yarn test # Vitest +script/develop # Development server +``` + +### Component Prefixes + +- `ha-` - Home Assistant components +- `hui-` - Lovelace UI components +- `dialog-` - Dialog components + +### Import Patterns + +```typescript +import type { HomeAssistant } from "../types"; +import { fireEvent } from "../common/dom/fire_event"; +import { showAlertDialog } from "../dialogs/generic/show-alert-dialog"; +``` + +## Core Architecture + +The Home Assistant frontend is a modern web application that: + +- Uses Web Components (custom elements) built with Lit framework +- Is written entirely in TypeScript with strict type checking +- Communicates with the backend via WebSocket API +- Provides comprehensive theming and internationalization + +## Development Standards + +### Code Quality Requirements + +**Linting and Formatting (Enforced by Tools)** + +- ESLint config extends Airbnb, TypeScript strict, Lit, Web Components, Accessibility +- Prettier with ES5 trailing commas enforced +- No console statements (`no-console: "error"`) - use proper logging +- Import organization: No unused imports, consistent type imports + +**Naming Conventions** + +- PascalCase for types and classes +- camelCase for variables, methods +- Private methods require leading underscore +- Public methods forbid leading underscore + +### TypeScript Usage + +- **Always use strict TypeScript**: Enable all strict flags, avoid `any` types +- **Proper type imports**: Use `import type` for type-only imports +- **Define interfaces**: Create proper interfaces for data structures +- **Type component properties**: All Lit properties must be properly typed +- **No unused variables**: Prefix with `_` if intentionally unused +- **Consistent imports**: Use `@typescript-eslint/consistent-type-imports` + +```typescript +// Good +import type { HomeAssistant } from "../types"; + +interface EntityConfig { + entity: string; + name?: string; +} + +@property({ type: Object }) +hass!: HomeAssistant; + +// Bad +@property() +hass: any; +``` + +### Web Components with Lit + +- **Use Lit 3.x patterns**: Follow modern Lit practices +- **Extend appropriate base classes**: Use `LitElement`, `SubscribeMixin`, or other mixins as needed +- **Define custom element names**: Use `ha-` prefix for components + +```typescript +@customElement("ha-my-component") +export class HaMyComponent extends LitElement { + @property({ attribute: false }) + hass!: HomeAssistant; + + @state() + private _config?: MyComponentConfig; + + static get styles() { + return css` + :host { + display: block; + } + `; + } + + render() { + return html`
    Content
    `; + } +} +``` + +### Component Guidelines + +- **Use composition**: Prefer composition over inheritance +- **Lazy load panels**: Heavy panels should be dynamically imported +- **Optimize renders**: Use `@state()` for internal state, `@property()` for public API +- **Handle loading states**: Always show appropriate loading indicators +- **Support themes**: Use CSS custom properties from theme + +### Data Management + +- **Use WebSocket API**: All backend communication via home-assistant-js-websocket +- **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 + +```typescript +// Good +try { + const result = await fetchEntityRegistry(this.hass.connection); + this._processResult(result); +} catch (err) { + showAlertDialog(this, { + text: `Failed to load: ${err.message}`, + }); +} +``` + +### Styling Guidelines + +- **Use CSS custom properties**: Leverage the theme system +- **Mobile-first responsive**: Design for mobile, enhance for desktop +- **Follow Material Design**: Use Material Web Components where appropriate +- **Support RTL**: Ensure all layouts work in RTL languages + +```typescript +static get styles() { + return css` + :host { + --spacing: 16px; + padding: var(--spacing); + color: var(--primary-text-color); + background-color: var(--card-background-color); + } + + @media (max-width: 600px) { + :host { + --spacing: 8px; + } + } + `; +} +``` + +### Performance Best Practices + +- **Code split**: Split code at the panel/dialog level +- **Lazy load**: Use dynamic imports for heavy components +- **Optimize bundle**: Keep initial bundle size minimal +- **Use virtual scrolling**: For long lists, implement virtual scrolling +- **Memoize computations**: Cache expensive calculations + +### Testing Requirements + +- **Write tests**: Add tests for data processing and utilities +- **Test with Vitest**: Use the established test framework +- **Mock appropriately**: Mock WebSocket connections and API calls +- **Test accessibility**: Ensure components are accessible + +## Component Library + +### Dialog Components + +**Available Dialog Types:** + +- `ha-md-dialog` - Preferred for new code (Material Design 3) +- `ha-dialog` - Legacy component still widely used + +**Opening Dialogs (Fire Event Pattern - Recommended):** + +```typescript +fireEvent(this, "show-dialog", { + dialogTag: "dialog-example", + dialogImport: () => import("./dialog-example"), + dialogParams: { title: "Example", data: someData }, +}); +``` + +**Dialog Implementation Requirements:** + +- Implement `HassDialog` interface +- Use `createCloseHeading()` for standard headers +- Import `haStyleDialog` for consistent styling +- Return `nothing` when no params (loading state) +- Fire `dialog-closed` event when closing +- Add `dialogInitialFocus` for accessibility + +```` + +### Form Component (ha-form) +- Schema-driven using `HaFormSchema[]` +- Supports entity, device, area, target, number, boolean, time, action, text, object, select, icon, media, location selectors +- Built-in validation with error display +- Use `dialogInitialFocus` in dialogs +- Use `computeLabel`, `computeError`, `computeHelper` for translations + +```typescript + this.hass.localize(`ui.panel.${schema.name}`)} + @value-changed=${this._valueChanged} +> +```` + +### Alert Component (ha-alert) + +- Types: `error`, `warning`, `info`, `success` +- Properties: `title`, `alert-type`, `dismissable`, `icon`, `action`, `rtl` +- Content announced by screen readers when dynamically displayed + +```html +Error message +Description +Success message +``` + +## Common Patterns + +### Creating a Panel + +```typescript +@customElement("ha-panel-myfeature") +export class HaPanelMyFeature extends SubscribeMixin(LitElement) { + @property({ attribute: false }) + hass!: HomeAssistant; + + @property({ type: Boolean, reflect: true }) + narrow!: boolean; + + @property() + route!: Route; + + hassSubscribe() { + return [ + subscribeEntityRegistry(this.hass.connection, (entities) => { + this._entities = entities; + }), + ]; + } +} +``` + +### Creating a Dialog + +```typescript +@customElement("dialog-my-feature") +export class DialogMyFeature + extends LitElement + implements HassDialog +{ + @property({ attribute: false }) + hass!: HomeAssistant; + + @state() + private _params?: MyDialogParams; + + public async showDialog(params: MyDialogParams): Promise { + this._params = params; + } + + public closeDialog(): void { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render() { + if (!this._params) { + return nothing; + } + + return html` + + + + ${this.hass.localize("ui.common.cancel")} + + + ${this.hass.localize("ui.common.save")} + + + `; + } + + static styles = [haStyleDialog, css``]; +} +``` + +### Dialog Design Guidelines + +- Max width: 560px (Alert/confirmation: 320px fixed width) +- Close X-icon on top left (all screen sizes) +- Submit button grouped with cancel at bottom right +- Keep button labels short: "Save", "Delete", "Enable" +- Destructive actions use red warning button +- Always use a title (best practice) +- Strive for minimalism + +#### Creating a Lovelace Card + +**Purpose**: Cards allow users to tell different stories about their house (based on gallery) + +```typescript +@customElement("hui-my-card") +export class HuiMyCard extends LitElement implements LovelaceCard { + @property({ attribute: false }) + hass!: HomeAssistant; + + @state() + private _config?: MyCardConfig; + + public setConfig(config: MyCardConfig): void { + if (!config.entity) { + throw new Error("Entity required"); + } + this._config = config; + } + + public getCardSize(): number { + return 3; // Height in grid units + } + + // Optional: Editor for card configuration + public static getConfigElement(): LovelaceCardEditor { + return document.createElement("hui-my-card-editor"); + } + + // Optional: Stub config for card picker + public static getStubConfig(): object { + return { entity: "" }; + } +} +``` + +**Card Guidelines:** + +- Cards are highly customizable for different households +- Implement `LovelaceCard` interface with `setConfig()` and `getCardSize()` +- Use proper error handling in `setConfig()` +- Consider all possible states (loading, error, unavailable) +- Support different entity types and states +- Follow responsive design principles +- Add configuration editor when needed + +### Internationalization + +- **Use localize**: Always use the localization system +- **Add translation keys**: Add keys to src/translations/en.json +- **Support placeholders**: Use proper placeholder syntax + +```typescript +this.hass.localize("ui.panel.config.updates.update_available", { + count: 5, +}); +``` + +### Accessibility + +- **ARIA labels**: Add appropriate ARIA labels +- **Keyboard navigation**: Ensure all interactions work with keyboard +- **Screen reader support**: Test with screen readers +- **Color contrast**: Meet WCAG AA standards + +## Development Workflow + +### Setup and Commands + +1. **Setup**: `script/setup` - Install dependencies +2. **Develop**: `script/develop` - Development server +3. **Lint**: `yarn lint` - Run all linting before committing +4. **Test**: `yarn test` - Add and run tests +5. **Build**: `script/build_frontend` - Test production build + +### Common Pitfalls to Avoid + +- Don't use `querySelector` - Use refs or component properties +- Don't manipulate DOM directly - Let Lit handle rendering +- Don't use global styles - Scope styles to components +- Don't block the main thread - Use web workers for heavy computation +- Don't ignore TypeScript errors - Fix all type issues + +### Security Best Practices + +- Sanitize HTML - Never use `unsafeHTML` with user content +- Validate inputs - Always validate user inputs +- Use HTTPS - All external resources must use HTTPS +- CSP compliance - Ensure code works with Content Security Policy + +### Text and Copy Guidelines + +#### Terminology Standards + +**Delete vs Remove** (Based on gallery/src/pages/Text/remove-delete-add-create.markdown) + +- **Use "Remove"** for actions that can be restored or reapplied: + - Removing a user's permission + - Removing a user from a group + - Removing links between items + - Removing a widget from dashboard + - Removing an item from a cart +- **Use "Delete"** for permanent, non-recoverable actions: + - Deleting a field + - Deleting a value in a field + - Deleting a task + - Deleting a group + - Deleting a permission + - Deleting a calendar event + +**Create vs Add** (Create pairs with Delete, Add pairs with Remove) + +- **Use "Add"** for already-existing items: + - Adding a permission to a user + - Adding a user to a group + - Adding links between items + - Adding a widget to dashboard + - Adding an item to a cart +- **Use "Create"** for something made from scratch: + - Creating a new field + - Creating a new task + - Creating a new group + - Creating a new permission + - Creating a new calendar event + +#### Writing Style (Consistent with Home Assistant Documentation) + +- **Use American English**: Standard spelling and terminology +- **Friendly, informational tone**: Be inspiring, personal, comforting, engaging +- **Address users directly**: Use "you" and "your" +- **Be inclusive**: Objective, non-discriminatory language +- **Be concise**: Use clear, direct language +- **Be consistent**: Follow established terminology patterns +- **Use active voice**: "Delete the automation" not "The automation should be deleted" +- **Avoid jargon**: Use terms familiar to home automation users + +#### Language Standards + +- **Always use "Home Assistant"** in full, never "HA" or "HASS" +- **Avoid abbreviations**: Spell out terms when possible +- **Use sentence case everywhere**: Titles, headings, buttons, labels, UI elements + - ✅ "Create new automation" + - ❌ "Create New Automation" + - ✅ "Device settings" + - ❌ "Device Settings" +- **Oxford comma**: Use in lists (item 1, item 2, and item 3) +- **Replace Latin terms**: Use "like" instead of "e.g.", "for example" instead of "i.e." +- **Avoid CAPS for emphasis**: Use bold or italics instead +- **Write for all skill levels**: Both technical and non-technical users + +#### Key Terminology + +- **"add-on"** (hyphenated, not "addon") +- **"integration"** (preferred over "component") +- **Technical terms**: Use lowercase (automation, entity, device, service) + +#### Translation Considerations + +- **Add translation keys**: All user-facing text must be translatable +- **Use placeholders**: Support dynamic content in translations +- **Keep context**: Provide enough context for translators + +```typescript +// Good +this.hass.localize("ui.panel.config.automation.delete_confirm", { + name: automation.alias, +}); + +// Bad - hardcoded text +("Are you sure you want to delete this automation?"); +``` + +### Common Review Issues (From PR Analysis) + +#### 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 + +#### 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 +- **Form prefilling**: Use smart defaults but allow user override + +#### Component Design Patterns + +- **Terminology consistency**: Use "Join"/"Apply" instead of "Group" when appropriate +- **Visual hierarchy**: Ensure proper font sizes and spacing ratios +- **Grid alignment**: Components should align to the design grid system +- **Badge placement**: Position badges and indicators consistently +- **Color theming**: Respect theme variables and design system colors + +#### Code Quality Issues + +- **Null checking**: Always check if entities exist before accessing properties +- **TypeScript safety**: Handle potentially undefined array/object access +- **Import organization**: Remove unused imports and use proper type imports +- **Event handling**: Properly subscribe and unsubscribe from events +- **Memory leaks**: Clean up subscriptions and event listeners + +#### Configuration and Props + +- **Optional parameters**: Make configuration fields optional when sensible +- **Smart defaults**: Provide reasonable default values +- **Future extensibility**: Design APIs that can be extended later +- **Validation**: Validate configuration before applying changes + +## Review Guidelines + +### Core Requirements Checklist + +- [ ] TypeScript strict mode passes (`yarn lint:types`) +- [ ] No ESLint errors or warnings (`yarn lint:eslint`) +- [ ] Prettier formatting applied (`yarn lint:prettier`) +- [ ] Lit analyzer passes (`yarn lint:lit`) +- [ ] Component follows Lit best practices +- [ ] Proper error handling implemented +- [ ] Loading states handled +- [ ] Mobile responsive +- [ ] Theme variables used +- [ ] Translations added +- [ ] Accessible to screen readers +- [ ] Tests added (where applicable) +- [ ] No console statements (use proper logging) +- [ ] Unused imports removed +- [ ] Proper naming conventions + +### Text and Copy Checklist + +- [ ] Follows terminology guidelines (Delete vs Remove, Create vs Add) +- [ ] Localization keys added for all user-facing text +- [ ] Uses "Home Assistant" (never "HA" or "HASS") +- [ ] Sentence case for ALL text (titles, headings, buttons, labels) +- [ ] American English spelling +- [ ] Friendly, informational tone +- [ ] Avoids abbreviations and jargon +- [ ] Correct terminology (add-on not addon, integration not component) + +### Component-Specific Checks + +- [ ] Dialogs implement HassDialog interface +- [ ] Dialog styling uses haStyleDialog +- [ ] Dialog accessibility includes dialogInitialFocus +- [ ] ha-alert used correctly for messages +- [ ] ha-form uses proper schema structure +- [ ] Components handle all states (loading, error, unavailable) +- [ ] Entity existence checked before property access +- [ ] Event subscriptions properly cleaned up diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000000..02dd134122 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +.github/copilot-instructions.md \ No newline at end of file From 1f8a9e4caf9c9814e68ad934fd7f6d5871b6a9ea Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 27 Jun 2025 10:38:11 -0700 Subject: [PATCH 155/216] Improve settings page accessibility (No. 2) (#25965) --- .../config/dashboard/ha-config-navigation.ts | 14 ++++++++++++++ src/panels/config/dashboard/ha-config-updates.ts | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/panels/config/dashboard/ha-config-navigation.ts b/src/panels/config/dashboard/ha-config-navigation.ts index 87b2f1bc62..01720f45b5 100644 --- a/src/panels/config/dashboard/ha-config-navigation.ts +++ b/src/panels/config/dashboard/ha-config-navigation.ts @@ -54,6 +54,9 @@ class HaConfigNavigation extends LitElement { `, })); return html` +
    + ${this.hass.localize("panel.config")} +
    ` : nothing}
    - ${deviceEntry ? computeDeviceNameDisplay(deviceEntry, this.hass) : entity.attributes.friendly_name} Date: Sat, 28 Jun 2025 17:40:47 +0200 Subject: [PATCH 156/216] Update dependency prettier to v3.6.1 (#25978) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 177d508d25..e1500b681f 100644 --- a/package.json +++ b/package.json @@ -210,7 +210,7 @@ "lodash.template": "4.5.0", "map-stream": "0.0.7", "pinst": "3.0.0", - "prettier": "3.6.0", + "prettier": "3.6.1", "rspack-manifest-plugin": "5.0.3", "serve": "14.2.4", "sinon": "21.0.0", diff --git a/yarn.lock b/yarn.lock index ef5f2b71fc..a8a807048f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9405,7 +9405,7 @@ __metadata: node-vibrant: "npm:4.0.3" object-hash: "npm:3.0.0" pinst: "npm:3.0.0" - prettier: "npm:3.6.0" + prettier: "npm:3.6.1" punycode: "npm:2.3.1" qr-scanner: "npm:1.4.2" qrcode: "npm:1.5.4" @@ -12244,12 +12244,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.6.0": - version: 3.6.0 - resolution: "prettier@npm:3.6.0" +"prettier@npm:3.6.1": + version: 3.6.1 + resolution: "prettier@npm:3.6.1" bin: prettier: bin/prettier.cjs - checksum: 10/5c0db5a8e32d2ac9824d8bc652990dfd534bc7a7c6f26d99d50c9146a2d9befb3cd1cc86c4aee71caf6b264d421a4b4b5961e31a62dda3790b8fec2521a76eef + checksum: 10/e2c4b47bf1bda4f932143e52e0f57239dd60cfef08296b56d4073426e2d310edc353a7daa5285a546bd3c1619c76894759cc98d82d4ac710f72a711ae2899bcc languageName: node linkType: hard From 44d87e3c663819db249e50f4fd8b4b7a52276191 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 28 Jun 2025 17:41:15 +0200 Subject: [PATCH 157/216] Update dependency barcode-detector to v3.0.5 (#25980) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index e1500b681f..2409db0577 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "@webcomponents/scoped-custom-element-registry": "0.0.10", "@webcomponents/webcomponentsjs": "2.8.0", "app-datepicker": "5.1.1", - "barcode-detector": "3.0.4", + "barcode-detector": "3.0.5", "color-name": "2.0.0", "comlink": "4.4.2", "core-js": "3.43.0", diff --git a/yarn.lock b/yarn.lock index a8a807048f..c7aaca96df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6067,12 +6067,12 @@ __metadata: languageName: node linkType: hard -"barcode-detector@npm:3.0.4": - version: 3.0.4 - resolution: "barcode-detector@npm:3.0.4" +"barcode-detector@npm:3.0.5": + version: 3.0.5 + resolution: "barcode-detector@npm:3.0.5" dependencies: - zxing-wasm: "npm:2.1.2" - checksum: 10/9570d854ad176d0fa6e82f800eace4a9d7ed9975ae7d10d3a1ea62c1094a35893f5c6bad7ace11442827d92c2233bb9f089a2ce11c5180dedf68099f8dfddc84 + zxing-wasm: "npm:2.2.0" + checksum: 10/12a35c115d7e578a290a66b6a4b924b20b0f0477d67973899163d59af78d5f0ac7e8fc0f2e5dff311478f6b0cb3daefbad4ebd7dde54bdead24d8432dc4084d1 languageName: node linkType: hard @@ -9347,7 +9347,7 @@ __metadata: app-datepicker: "npm:5.1.1" babel-loader: "npm:10.0.0" babel-plugin-template-html-minifier: "npm:4.1.0" - barcode-detector: "npm:3.0.4" + barcode-detector: "npm:3.0.5" browserslist-useragent-regexp: "npm:4.1.3" color-name: "npm:2.0.0" comlink: "npm:4.4.2" @@ -16012,14 +16012,14 @@ __metadata: languageName: node linkType: hard -"zxing-wasm@npm:2.1.2": - version: 2.1.2 - resolution: "zxing-wasm@npm:2.1.2" +"zxing-wasm@npm:2.2.0": + version: 2.2.0 + resolution: "zxing-wasm@npm:2.2.0" dependencies: "@types/emscripten": "npm:^1.40.1" type-fest: "npm:^4.41.0" peerDependencies: "@types/emscripten": ">=1.39.6" - checksum: 10/3b0476b4779fdb22de599022f6a8c51c77834422863d7412af35b3a7c682a48ac414ee6a3aafa264707b9beeb95fc20d933331f1d33b19d70c762509d5d1d5e6 + checksum: 10/95cd6ec75247116ce6a57b1c261b5bd900e1888e4e6945d73d03ae0ae1b31b12b5955b995da85e03c0fa366f45b8859cf1dd4d2d142d0d7985abd68e48a4f430 languageName: node linkType: hard From 6d9e6a616d9a827f7943d4e273656e89dc90472f Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Sun, 29 Jun 2025 09:25:29 +0200 Subject: [PATCH 158/216] Fix sentence-casing, spelling and grammar issues (#25981) * Fix sentence-casing, spelling and grammar issues * Add "IP information" to the list * More sentence-casing issues --- src/translations/en.json | 98 ++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index a9ad96ce5e..c740dddf8d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -51,7 +51,7 @@ "owner": "Owner", "system-admin": "Administrators", "system-users": "Users", - "system-read-only": "Read-Only Users" + "system-read-only": "Read-only users" }, "config_entry": { "disabled_by": { @@ -471,7 +471,7 @@ "radius_meters": "[%key:ui::panel::config::core::section::core::core_config::elevation_meters%]" }, "selector": { - "options": "Selector Options", + "options": "Selector options", "types": { "action": "Action", "area": "Area", @@ -480,7 +480,7 @@ "color_temp": "Color temperature", "condition": "Condition", "date": "Date", - "datetime": "Date and Time", + "datetime": "Date and time", "device": "Device", "duration": "Duration", "entity": "Entity", @@ -490,7 +490,7 @@ "media": "Media", "number": "Number", "object": "Object", - "color_rgb": "RGB Color", + "color_rgb": "RGB color", "select": "Select", "state": "State", "target": "Target", @@ -498,7 +498,7 @@ "text": "Text", "theme": "Theme", "time": "Time", - "manual": "Manual Entry" + "manual": "Manual entry" } }, "template": { @@ -857,7 +857,7 @@ "cyan": "Cyan", "teal": "Teal", "green": "Green", - "light-green": "Light Green", + "light-green": "Light green", "lime": "Lime", "yellow": "Yellow", "amber": "Amber", @@ -1045,7 +1045,7 @@ "podcast": "Podcast", "season": "Season", "track": "Track", - "tv_show": "TV Show", + "tv_show": "TV show", "url": "URL", "video": "Video" }, @@ -2935,7 +2935,7 @@ "name": "Name", "description": "Description", "tag_id": "Tag ID", - "tag_id_placeholder": "Autogenerated if left empty", + "tag_id_placeholder": "Auto-generated if left empty", "delete": "Delete", "update": "Update", "create": "Create", @@ -4490,14 +4490,14 @@ "trace_no_longer_available": "Chosen trace is no longer available", "enter_downloaded_trace": "Enter downloaded trace", "tabs": { - "details": "Step Details", - "timeline": "Trace Timeline", + "details": "Step details", + "timeline": "Trace timeline", "logbook": "Related logbook entries", - "automation_config": "Automation Config", - "step_config": "Step Config", - "changed_variables": "Changed Variables", - "blueprint_config": "Blueprint Config", - "script_config": "Script Config" + "automation_config": "Automation config", + "step_config": "Step config", + "changed_variables": "Changed variables", + "blueprint_config": "Blueprint config", + "script_config": "Script config" }, "path": { "choose": "Select a step on the left for more information.", @@ -4544,7 +4544,7 @@ "caption": "Blueprints", "description": "Manage blueprints", "overview": { - "header": "Blueprint Editor", + "header": "Blueprint editor", "introduction": "The blueprint configuration allows you to import and manage your blueprints.", "learn_more": "Learn more about using blueprints", "headers": { @@ -4601,14 +4601,14 @@ "override_description": "Importing it will override the existing blueprint. If the updated blueprint is not compatible, it can break your automations. Automations will have to be adjusted manually.", "error_no_url": "Please enter the blueprint address.", "unsupported_blueprint": "This blueprint is not supported", - "file_name": "Blueprint Path" + "file_name": "Blueprint path" } }, "script": { "caption": "Scripts", "description": "Execute a sequence of actions", "picker": { - "header": "Script Editor", + "header": "Script editor", "introduction": "The script editor allows you to create and edit scripts. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.", "learn_more": "Learn more about scripts", "no_scripts": "We couldn't find any scripts", @@ -4684,7 +4684,7 @@ "field_delete_confirm_title": "Delete field?", "field_delete_confirm_text": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm_text%]", "header": "Script: {name}", - "default_name": "New Script", + "default_name": "New script", "modes": { "label": "[%key:ui::panel::config::automation::editor::modes::label%]", "learn_more": "[%key:ui::panel::config::automation::editor::modes::learn_more%]", @@ -4729,7 +4729,7 @@ "description": "Capture device states and easily recall them later", "activated": "Activated scene {name}.", "picker": { - "header": "Scene Editor", + "header": "Scene editor", "introduction": "The scene editor allows you to create and edit scenes. Please follow the link below to read the instructions to make sure that you have configured Home Assistant correctly.", "learn_more": "Learn more about scenes", "pick_scene": "Pick scene to edit", @@ -4993,7 +4993,7 @@ "other_home_assistant": "Other Home Assistant", "instance_name": "Instance name", "instance_version": "Instance version", - "ip_address": "IP Address", + "ip_address": "IP address", "connected_at": "Connected at", "obfuscated_ip": { "show": "Show IP address", @@ -5661,9 +5661,9 @@ }, "dhcp": { "title": "DHCP discovery", - "mac_address": "MAC Address", + "mac_address": "MAC address", "hostname": "Hostname", - "ip_address": "IP Address", + "ip_address": "IP address", "no_devices_found": "No recent DHCP requests found; no matching discoveries detected" }, "thread": { @@ -5722,7 +5722,7 @@ "name": "Name", "type": "Type", "port": "Port", - "ip_addresses": "IP Addresses", + "ip_addresses": "IP addresses", "properties": "Properties", "discovery_information": "Discovery information", "copy_to_clipboard": "Copy to clipboard", @@ -6354,7 +6354,7 @@ "title": "Door lock", "twist_assist": "Twist assist", "block_to_block": "Block to block", - "auto_relock_time": "Auto relock time", + "auto_relock_time": "Autorelock time", "hold_release_time": "Hold and release time", "operation_type": "Operation type", "operation_types": { @@ -6525,13 +6525,13 @@ "prefix": "Subnet prefix", "add_address": "Add address", "gateway": "Gateway address", - "dns_server": "DNS Server", - "add_dns_server": "Add DNS Server", + "dns_server": "DNS server", + "add_dns_server": "Add DNS server", "custom_dns": "Custom", "unsaved": "You have unsaved changes, these will get lost if you change tabs, do you want to continue?", "failed_to_change": "Failed to change network settings", "hostname": { - "title": "Host name", + "title": "Hostname", "description": "The name your instance will have on your network", "failed_to_set_hostname": "Setting hostname failed" } @@ -6548,9 +6548,9 @@ }, "network_adapter": "Network adapter", "network_adapter_info": "Configure which network adapters integrations will use. A restart is required for these settings to apply.", - "ip_information": "IP Information", + "ip_information": "IP information", "adapter": { - "auto_configure": "Auto configure", + "auto_configure": "Autoconfigure", "detected": "Detected", "adapter": "Adapter" } @@ -6559,7 +6559,7 @@ "caption": "Storage", "description": "{percent_used} used - {free_space} free", "used_space": "Used space", - "emmc_lifetime_used": "eMMC Lifetime Used", + "emmc_lifetime_used": "eMMC lifetime used", "disk_metrics": "Disk metrics", "datadisk": { "title": "Move data disk", @@ -6910,7 +6910,7 @@ }, "edit_view": { "header": "View configuration", - "header_name": "{name} View Configuration", + "header_name": "{name} view configuration", "add": "Add view", "background": { "settings": "Background settings", @@ -7026,7 +7026,7 @@ }, "edit_card": { "header": "Card configuration", - "typed_header": "{type} Card configuration", + "typed_header": "{type} card configuration", "pick_card": "Add to dashboard", "pick_card_title": "Which card would you like to add to {name}", "toggle_editor": "Toggle editor", @@ -7096,7 +7096,7 @@ "move_card": { "header": "Choose a view to move the card to", "strategy_error_title": "Impossible to move the card", - "strategy_error_text_strategy": "Moving a card to an auto generated view is not supported.", + "strategy_error_text_strategy": "Moving a card to an auto-generated view is not supported.", "success": "Card moved successfully", "error": "Error while moving card" }, @@ -7519,13 +7519,13 @@ "geo_location_sources": "Geolocation sources", "no_geo_location_sources": "No geolocation sources available", "appearance": "Appearance", - "theme_mode": "Theme Mode", + "theme_mode": "Theme mode", "theme_modes": { "auto": "Auto", "light": "Light", "dark": "Dark" }, - "default_zoom": "Default Zoom", + "default_zoom": "Default zoom", "source": "Source", "description": "The Map card that allows you to display entities on a map." }, @@ -7573,7 +7573,7 @@ "picture-elements": { "name": "Picture elements", "description": "The Picture elements card is one of the most versatile types of cards. The cards allow you to position icons or text and even actions! On an image based on coordinates.", - "card_options": "Card Options", + "card_options": "Card options", "elements": "Elements", "new_element": "Add new element", "confirm_delete_element": "Are you sure you want to delete the {type} element?", @@ -7622,14 +7622,14 @@ "integration_not_loaded": "This card requires the `todo` integration to be set up.", "hide_completed": "Hide completed items", "hide_create": "Hide 'Add item' field", - "display_order": "Display Order", + "display_order": "Display order", "sort_modes": { "none": "Default", "manual": "Manual", "alpha_asc": "Alphabetical (A-Z)", "alpha_desc": "Alphabetical (Z-A)", - "duedate_asc": "Due Date (Soonest First)", - "duedate_desc": "Due Date (Latest First)" + "duedate_asc": "Due date (Soonest first)", + "duedate_desc": "Due date (Latest first)" } }, "thermostat": { @@ -8231,14 +8231,14 @@ "confirm_delete_title": "Delete long-lived access token?", "confirm_delete_text": "Are you sure you want to delete the long-lived access token for {name}?", "delete_failed": "Failed to delete the access token.", - "create": "Create Token", + "create": "Create token", "create_failed": "Failed to create the access token.", "name": "Name", "prompt_name": "Give the token a name", "prompt_copy_token": "Copy your access token. It will not be shown again.", "empty_state": "You have no long-lived access tokens yet.", "qr_code_image": "QR code for token {name}", - "generate_qr_code": "Generate QR Code" + "generate_qr_code": "Generate QR code" } }, "todo": { @@ -8395,7 +8395,7 @@ "hdmi_input": "HDMI input", "hdmi_switcher": "HDMI switcher", "volume": "Volume", - "total_tv_time": "Total TV Time", + "total_tv_time": "Total TV time", "turn_tv_off": "Turn television off", "air": "Air" }, @@ -9076,7 +9076,7 @@ }, "capability": { "stage": { - "title": "Add-on Stage", + "title": "Add-on stage", "description": "Add-ons can have one of three stages:\n\n{icon_stable} **Stable**: These are add-ons ready to be used in production.\n\n{icon_experimental} **Experimental**: These may contain bugs, and may be unfinished.\n\n{icon_deprecated} **Deprecated**: These add-ons will no longer receive any updates." }, "rating": { @@ -9158,8 +9158,8 @@ "description": "This will restart the add-on if it crashes" }, "auto_update": { - "title": "Auto update", - "description": "Auto update the add-on when there is a new version available" + "title": "Autoupdate", + "description": "Autoupdate the add-on when there is a new version available" }, "ingress_panel": { "title": "Show in sidebar", @@ -9270,7 +9270,7 @@ "addons": "Add-ons", "dashboard": "Dashboard", "backups": "Backups", - "store": "Add-on Store", + "store": "Add-on store", "system": "System" }, "my": { @@ -9360,7 +9360,7 @@ "hostname": "Hostname", "change_hostname": "Change hostname", "new_hostname": "Please enter a new hostname:", - "ip_address": "IP Address", + "ip_address": "IP address", "change": "Change", "operating_system": "Operating system", "docker_version": "Docker version", @@ -9412,7 +9412,7 @@ "confirm_password": "Confirm encryption key", "password_protection": "Password protection", "enter_password": "Please enter a password.", - "passwords_not_matching": "The passwords does not match", + "passwords_not_matching": "The passwords do not match", "backup_already_running": "A backup or restore is already running. Creating a new backup is currently not possible, try again later.", "confirm_restore_partial_backup_title": "Restore partial backup", "confirm_restore_partial_backup_text": "The backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again.", From 7187e25cadaf12a5a495c0435e378b60576cdf79 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 11:06:34 +0200 Subject: [PATCH 159/216] Update rspack monorepo to v1.4.0 (#25987) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 260 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 162 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index 2409db0577..94297453e7 100644 --- a/package.json +++ b/package.json @@ -159,8 +159,8 @@ "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", "@rsdoctor/rspack-plugin": "1.1.4", - "@rspack/cli": "1.3.12", - "@rspack/core": "1.3.12", + "@rspack/cli": "1.4.0", + "@rspack/core": "1.4.0", "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.22", "@types/chromecast-caf-sender": "1.0.11", diff --git a/yarn.lock b/yarn.lock index c7aaca96df..e9eddabefc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1324,6 +1324,34 @@ __metadata: languageName: node linkType: hard +"@emnapi/core@npm:^1.4.3": + version: 1.4.3 + resolution: "@emnapi/core@npm:1.4.3" + dependencies: + "@emnapi/wasi-threads": "npm:1.0.2" + tslib: "npm:^2.4.0" + checksum: 10/b511f66b897d2019835391544fdf11f4fa0ce06cc1181abfa17c7d4cf03aaaa4fc8a64fcd30bb3f901de488d0a6f370b53a8de2215a898f5a4ac98015265b3b7 + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.4.3": + version: 1.4.3 + resolution: "@emnapi/runtime@npm:1.4.3" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/4f90852a1a5912982cc4e176b6420556971bcf6a85ee23e379e2455066d616219751367dcf43e6a6eaf41ea7e95ba9dc830665a52b5d979dfe074237d19578f8 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.0.2": + version: 1.0.2 + resolution: "@emnapi/wasi-threads@npm:1.0.2" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/e82941776665eb958c2084728191d6b15a94383449975c4621b67a1c8217e1c0ec11056a693906c76863cb96f782f8be500510ecec6874e3f5da35a8e7968cfd + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.25.5": version: 0.25.5 resolution: "@esbuild/aix-ppc64@npm:0.25.5" @@ -3195,58 +3223,69 @@ __metadata: languageName: node linkType: hard -"@module-federation/error-codes@npm:0.14.0": - version: 0.14.0 - resolution: "@module-federation/error-codes@npm:0.14.0" - checksum: 10/61fcaa4d628676e252fd38d6c397575d9ecbf6f9ea145d2968a9f16b6210e624ef546706dbf9414b0756495e68cee21fd0693f6c931a0717781225c60d66b685 +"@module-federation/error-codes@npm:0.15.0": + version: 0.15.0 + resolution: "@module-federation/error-codes@npm:0.15.0" + checksum: 10/1dace0ecc511c6bf8b4f76db237a28f3f96b2f30a278d758afa70be4daccae1fc89613685fc66d85a74e82244a4cb1acd45cca7005a414d2431950d2035ea585 languageName: node linkType: hard -"@module-federation/runtime-core@npm:0.14.0": - version: 0.14.0 - resolution: "@module-federation/runtime-core@npm:0.14.0" +"@module-federation/runtime-core@npm:0.15.0": + version: 0.15.0 + resolution: "@module-federation/runtime-core@npm:0.15.0" dependencies: - "@module-federation/error-codes": "npm:0.14.0" - "@module-federation/sdk": "npm:0.14.0" - checksum: 10/ec011998c49fa476e6522708c77c938e54e7e1d126affbeb1a1e5ace84e832c4df6ab5222e0353f7b96177ee5bc168dc5d3083dd12603a6a53a5cb8b38b32953 + "@module-federation/error-codes": "npm:0.15.0" + "@module-federation/sdk": "npm:0.15.0" + checksum: 10/40dc67f8c496352f1883a8a520fd145996dc08cc640f1b337c6168d6e2be05ac5fb04fb3916f94300c9bcd15575a53eeb8fa472214d9d3b14906b24e00a47dd2 languageName: node linkType: hard -"@module-federation/runtime-tools@npm:0.14.0": - version: 0.14.0 - resolution: "@module-federation/runtime-tools@npm:0.14.0" +"@module-federation/runtime-tools@npm:0.15.0": + version: 0.15.0 + resolution: "@module-federation/runtime-tools@npm:0.15.0" dependencies: - "@module-federation/runtime": "npm:0.14.0" - "@module-federation/webpack-bundler-runtime": "npm:0.14.0" - checksum: 10/6056490081a6476ed78d4570df82e58f4bbb05d773934586a29074ab735c572d8913e6e1eaf6db7c0556b35172d2b2f5f9be1c3390f45454fb58686f2870c9b6 + "@module-federation/runtime": "npm:0.15.0" + "@module-federation/webpack-bundler-runtime": "npm:0.15.0" + checksum: 10/399a3092090b81c1e08b4a25a34d813b023241fbe5df692d7ff9a4d2365ab487f1974e22bd321ff16fa7df88580e0e403e6f7bf5e38fc679d251dd936c2d8eda languageName: node linkType: hard -"@module-federation/runtime@npm:0.14.0": - version: 0.14.0 - resolution: "@module-federation/runtime@npm:0.14.0" +"@module-federation/runtime@npm:0.15.0": + version: 0.15.0 + resolution: "@module-federation/runtime@npm:0.15.0" dependencies: - "@module-federation/error-codes": "npm:0.14.0" - "@module-federation/runtime-core": "npm:0.14.0" - "@module-federation/sdk": "npm:0.14.0" - checksum: 10/d5275afe8e0449e5374d8c4dec8c18547d011fbd0532c175b59b153ab990386b04789397e1385773ad39f64c5402987c75a20f2d1f7b2da40e7dddf2b313d2f7 + "@module-federation/error-codes": "npm:0.15.0" + "@module-federation/runtime-core": "npm:0.15.0" + "@module-federation/sdk": "npm:0.15.0" + checksum: 10/01362d8b2889eaae07b87735ae99d302c256a75f4ec7f7757d70684ab69c9ecbd4abe52736a43d7d2480ca9b42dca9a328356a811c5bdb11c8cf24506fe1dfce languageName: node linkType: hard -"@module-federation/sdk@npm:0.14.0": - version: 0.14.0 - resolution: "@module-federation/sdk@npm:0.14.0" - checksum: 10/2b430cdcf4741a24e1630e43577c9ca4c8d5c3112db3bc6158facdb0a6e240adce7c53a0cc4487a5d6f1767e31363f3846d8c73a2a3e21c748948d4739abc66b +"@module-federation/sdk@npm:0.15.0": + version: 0.15.0 + resolution: "@module-federation/sdk@npm:0.15.0" + checksum: 10/2d4bdc38d7fadca59f7e73c152e2614ef0228de1fd996c0143e81e68cf046cb20fb8b60df22931c5082b3ec891ce9448378fa38f647bb41d9222c877aa581152 languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:0.14.0": - version: 0.14.0 - resolution: "@module-federation/webpack-bundler-runtime@npm:0.14.0" +"@module-federation/webpack-bundler-runtime@npm:0.15.0": + version: 0.15.0 + resolution: "@module-federation/webpack-bundler-runtime@npm:0.15.0" dependencies: - "@module-federation/runtime": "npm:0.14.0" - "@module-federation/sdk": "npm:0.14.0" - checksum: 10/30e3403fcb48bbacac436f884303e538ff8023005efe4850515be2c3806ce81a065903f483a04c03fca3325671b439c10c07a4232c4817b9957ac24117febe7f + "@module-federation/runtime": "npm:0.15.0" + "@module-federation/sdk": "npm:0.15.0" + checksum: 10/a5171a591e320da36ad4edc6239faa34039f2996de8479465bcd76cfd5a406000d3040b4247c127e1151aaa80c5846832c6274a3ed8e90ebbd70959bfb18c4af + languageName: node + linkType: hard + +"@napi-rs/wasm-runtime@npm:^0.2.11": + version: 0.2.11 + resolution: "@napi-rs/wasm-runtime@npm:0.2.11" + dependencies: + "@emnapi/core": "npm:^1.4.3" + "@emnapi/runtime": "npm:^1.4.3" + "@tybys/wasm-util": "npm:^0.9.0" + checksum: 10/e30fe3060474c5018e160231df0531d62b5e22f4736ecd49c04ca6cadacb2acf59b9205435794cd5b898e41e2e3ddb6523e93b97799bd1f4d0751557de6e38e4 languageName: node linkType: hard @@ -3896,82 +3935,92 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-darwin-arm64@npm:1.3.12" +"@rspack/binding-darwin-arm64@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-darwin-arm64@npm:1.4.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-darwin-x64@npm:1.3.12" +"@rspack/binding-darwin-x64@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-darwin-x64@npm:1.4.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.3.12" +"@rspack/binding-linux-arm64-gnu@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.4.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.3.12" +"@rspack/binding-linux-arm64-musl@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.4.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.3.12" +"@rspack/binding-linux-x64-gnu@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.4.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-linux-x64-musl@npm:1.3.12" +"@rspack/binding-linux-x64-musl@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-linux-x64-musl@npm:1.4.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.3.12" +"@rspack/binding-wasm32-wasi@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-wasm32-wasi@npm:1.4.0" + dependencies: + "@napi-rs/wasm-runtime": "npm:^0.2.11" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@rspack/binding-win32-arm64-msvc@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.4.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.3.12" +"@rspack/binding-win32-ia32-msvc@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.4.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.3.12" +"@rspack/binding-win32-x64-msvc@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.4.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/binding@npm:1.3.12" +"@rspack/binding@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/binding@npm:1.4.0" dependencies: - "@rspack/binding-darwin-arm64": "npm:1.3.12" - "@rspack/binding-darwin-x64": "npm:1.3.12" - "@rspack/binding-linux-arm64-gnu": "npm:1.3.12" - "@rspack/binding-linux-arm64-musl": "npm:1.3.12" - "@rspack/binding-linux-x64-gnu": "npm:1.3.12" - "@rspack/binding-linux-x64-musl": "npm:1.3.12" - "@rspack/binding-win32-arm64-msvc": "npm:1.3.12" - "@rspack/binding-win32-ia32-msvc": "npm:1.3.12" - "@rspack/binding-win32-x64-msvc": "npm:1.3.12" + "@rspack/binding-darwin-arm64": "npm:1.4.0" + "@rspack/binding-darwin-x64": "npm:1.4.0" + "@rspack/binding-linux-arm64-gnu": "npm:1.4.0" + "@rspack/binding-linux-arm64-musl": "npm:1.4.0" + "@rspack/binding-linux-x64-gnu": "npm:1.4.0" + "@rspack/binding-linux-x64-musl": "npm:1.4.0" + "@rspack/binding-wasm32-wasi": "npm:1.4.0" + "@rspack/binding-win32-arm64-msvc": "npm:1.4.0" + "@rspack/binding-win32-ia32-msvc": "npm:1.4.0" + "@rspack/binding-win32-x64-msvc": "npm:1.4.0" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -3985,22 +4034,24 @@ __metadata: optional: true "@rspack/binding-linux-x64-musl": optional: true + "@rspack/binding-wasm32-wasi": + optional: true "@rspack/binding-win32-arm64-msvc": optional: true "@rspack/binding-win32-ia32-msvc": optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/9e9fccb374967526a183a2ce69c175c793d3a29f3949e4d2d865d2e118d14ea23cc4201735a51ac47107c16dc487c12e3f1e6a2ea7ba512651f4e04b0cdc90cb + checksum: 10/7eeaa873f1d7305e271523d441f9f58ac05489d5dce9c4e435ff6a0ea060a37456cba7b485809dbdd797529603f1c2ee760e888cb18e022dc5f523116c85b8b0 languageName: node linkType: hard -"@rspack/cli@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/cli@npm:1.3.12" +"@rspack/cli@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/cli@npm:1.4.0" dependencies: "@discoveryjs/json-ext": "npm:^0.5.7" - "@rspack/dev-server": "npm:1.1.2" + "@rspack/dev-server": "npm:~1.1.3" colorette: "npm:2.0.20" exit-hook: "npm:^4.0.0" interpret: "npm:^3.1.1" @@ -4011,39 +4062,38 @@ __metadata: "@rspack/core": ^1.0.0-alpha || ^1.x bin: rspack: bin/rspack.js - checksum: 10/6afe218b1838b0f6885d0117513004b700fb596cc8d63e85374356f0821f7de96dfc617ba9b284be90ffb24ebe1a79094844eb4a1280bf88239d8b3d1adaa0c0 + checksum: 10/9dd8f0aeea442177d5fbf3fd69a30810cae58d1a1c8e6cb7526b89e6c20dc573eada9ec3f9a7a9b122290b85c3d09eb707f5ac85c083147c92d425d1e89813b6 languageName: node linkType: hard -"@rspack/core@npm:1.3.12": - version: 1.3.12 - resolution: "@rspack/core@npm:1.3.12" +"@rspack/core@npm:1.4.0": + version: 1.4.0 + resolution: "@rspack/core@npm:1.4.0" dependencies: - "@module-federation/runtime-tools": "npm:0.14.0" - "@rspack/binding": "npm:1.3.12" + "@module-federation/runtime-tools": "npm:0.15.0" + "@rspack/binding": "npm:1.4.0" "@rspack/lite-tapable": "npm:1.0.1" - caniuse-lite: "npm:^1.0.30001718" peerDependencies: "@swc/helpers": ">=0.5.1" peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/9e5b12f889d3f95f0f2b5598ae15979d6a9f3a513bea05df6e5a6687d741c5a7c157a85eebfa41433e283b57c7b5501a9658bcd06fbecf31284385d818932306 + checksum: 10/ac4000b8ed9d0ef605199bcb615ae07de8105f3bc69f29f82679d89a79a18c92a03b5271bff2a923afba5dd255202ebf98c4e980b7707338291c740baca7929c languageName: node linkType: hard -"@rspack/dev-server@npm:1.1.2": - version: 1.1.2 - resolution: "@rspack/dev-server@npm:1.1.2" +"@rspack/dev-server@npm:~1.1.3": + version: 1.1.3 + resolution: "@rspack/dev-server@npm:1.1.3" dependencies: chokidar: "npm:^3.6.0" - http-proxy-middleware: "npm:^2.0.7" + http-proxy-middleware: "npm:^2.0.9" p-retry: "npm:^6.2.0" - webpack-dev-server: "npm:5.2.0" + webpack-dev-server: "npm:5.2.2" ws: "npm:^8.18.0" peerDependencies: "@rspack/core": "*" - checksum: 10/8dc8e806fae3f484b34eb6638c0443f8b3b9727efb8ccdd778a43c237e7dc4c04695cdffaed706a65354cb93957a60d4b42c23b7eb19155ee352aa0c914670d4 + checksum: 10/31cef80a602acf9468a94c31b1f09239cae9c47cf0ed25c6bcddd057bbaff5a220a1dead068a255d35d0addfea21d81a574069d63ea258c2807d84a02d4fe966 languageName: node linkType: hard @@ -4307,6 +4357,15 @@ __metadata: languageName: node linkType: hard +"@tybys/wasm-util@npm:^0.9.0": + version: 0.9.0 + resolution: "@tybys/wasm-util@npm:0.9.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/aa58e64753a420ad1eefaf7bacef3dda61d74f9336925943d9244132d5b48d9242f734f1e707fd5ccfa6dd1d8ec8e6debc234b4dedb3a5b0d8486d1f373350b2 + languageName: node + linkType: hard + "@types/babel__plugin-transform-runtime@npm:7.9.5": version: 7.9.5 resolution: "@types/babel__plugin-transform-runtime@npm:7.9.5" @@ -4450,7 +4509,7 @@ __metadata: languageName: node linkType: hard -"@types/express-serve-static-core@npm:^4.17.33": +"@types/express-serve-static-core@npm:^4.17.21, @types/express-serve-static-core@npm:^4.17.33": version: 4.19.6 resolution: "@types/express-serve-static-core@npm:4.19.6" dependencies: @@ -9312,8 +9371,8 @@ __metadata: "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" "@rsdoctor/rspack-plugin": "npm:1.1.4" - "@rspack/cli": "npm:1.3.12" - "@rspack/core": "npm:1.3.12" + "@rspack/cli": "npm:1.4.0" + "@rspack/core": "npm:1.4.0" "@shoelace-style/shoelace": "npm:2.20.1" "@swc/helpers": "npm:0.5.17" "@thomasloven/round-slider": "npm:0.6.0" @@ -9589,7 +9648,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-middleware@npm:^2.0.7": +"http-proxy-middleware@npm:^2.0.9": version: 2.0.9 resolution: "http-proxy-middleware@npm:2.0.9" dependencies: @@ -15174,13 +15233,14 @@ __metadata: languageName: node linkType: hard -"webpack-dev-server@npm:5.2.0": - version: 5.2.0 - resolution: "webpack-dev-server@npm:5.2.0" +"webpack-dev-server@npm:5.2.2": + version: 5.2.2 + resolution: "webpack-dev-server@npm:5.2.2" dependencies: "@types/bonjour": "npm:^3.5.13" "@types/connect-history-api-fallback": "npm:^1.5.4" "@types/express": "npm:^4.17.21" + "@types/express-serve-static-core": "npm:^4.17.21" "@types/serve-index": "npm:^1.9.4" "@types/serve-static": "npm:^1.15.5" "@types/sockjs": "npm:^0.3.36" @@ -15193,7 +15253,7 @@ __metadata: connect-history-api-fallback: "npm:^2.0.0" express: "npm:^4.21.2" graceful-fs: "npm:^4.2.6" - http-proxy-middleware: "npm:^2.0.7" + http-proxy-middleware: "npm:^2.0.9" ipaddr.js: "npm:^2.1.0" launch-editor: "npm:^2.6.1" open: "npm:^10.0.3" @@ -15214,7 +15274,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: 10/f93ca46b037e547a9db157db72ef98ab177659ad13a6e63302d87bd77b32e524dd7133f1ad18f5a51ec68712911c59be8d4e06aa7bcbe6f56a9e9ce3774cf7f6 + checksum: 10/59517409cd38c01a875a03b9658f3d20d492b5b8bead9ded4a0f3d33e6857daf2d352fe89f0181dcaea6d0fbe84b0494cb4750a87120fe81cdbb3c32b499451c languageName: node linkType: hard From 62eec56e5f524ba93750b2f1ea8d59b07af5a9f4 Mon Sep 17 00:00:00 2001 From: Norbert Rittel Date: Sun, 29 Jun 2025 17:16:09 +0200 Subject: [PATCH 160/216] Fix grammar of Light, Sensor and Tile card descriptions (#25988) * Fix grammar of Light, Sensor and Entity card descriptions * Capitalize "Tile card" as a name * Apply same change to Entity badge description --- src/translations/en.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index c740dddf8d..44a10730c9 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -7431,7 +7431,7 @@ }, "light": { "name": "Light", - "description": "The Light card allows you to change the brightness of the light." + "description": "The Light card allows you to change the brightness of a light." }, "generic": { "alt_text": "Alternative text", @@ -7612,7 +7612,7 @@ "none": "None", "line": "Line" }, - "description": "The Sensor card gives you a quick overview of your sensors state with an optional graph to visualize change over time.", + "description": "The Sensor card gives you a quick overview of a sensor's state with an optional graph to visualize change over time.", "limit_min": "Minimum value", "limit_max": "Maximum value" }, @@ -7639,7 +7639,7 @@ }, "tile": { "name": "Tile", - "description": "The tile card gives you a quick overview of your entity. The card allows you to toggle the entity, show the More info dialog or trigger custom actions.", + "description": "The Tile card gives you a quick overview of an entity. The card allows you to toggle the entity, show the More info dialog or trigger custom actions.", "color": "Color", "color_helper": "Inactive state (e.g. off, closed) will not be colored.", "icon_tap_action": "Icon tap behavior", @@ -7693,7 +7693,7 @@ "badge": { "entity": { "name": "Entity", - "description": "The Entity badge gives you a quick overview of your entity.", + "description": "The Entity badge gives you a quick overview of an entity.", "color": "[%key:ui::panel::lovelace::editor::card::tile::color%]", "color_helper": "[%key:ui::panel::lovelace::editor::card::tile::color_helper%]", "show_entity_picture": "Show entity picture", From dd18ad96f365b5255fb3edf5a697fb3a1ca91c7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 17:16:40 +0200 Subject: [PATCH 161/216] Update dependency gulp-rename to v2.1.0 (#25985) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 94297453e7..e74e17356d 100644 --- a/package.json +++ b/package.json @@ -199,7 +199,7 @@ "gulp": "5.0.1", "gulp-brotli": "3.0.0", "gulp-json-transform": "0.5.0", - "gulp-rename": "2.0.0", + "gulp-rename": "2.1.0", "html-minifier-terser": "7.2.0", "husky": "9.1.7", "jsdom": "26.1.0", diff --git a/yarn.lock b/yarn.lock index e9eddabefc..71448f7bf5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9158,10 +9158,10 @@ __metadata: languageName: node linkType: hard -"gulp-rename@npm:2.0.0": - version: 2.0.0 - resolution: "gulp-rename@npm:2.0.0" - checksum: 10/b9add0d130487dee6067206eebfc3867e4e254117edef154e8c270e3111b112335439ac1cac1519d6a32541343e04bd299484253a333b4e34c7d430039953e99 +"gulp-rename@npm:2.1.0": + version: 2.1.0 + resolution: "gulp-rename@npm:2.1.0" + checksum: 10/83be4ad1e8f4f8525681011695f02a9edbd50a37a08b8bdf9c0f01c67f317f8a5e50d1d693d65e3e23fb95669e7e22b4a9bbcd94eed46c3be9ad7e1be532d48b languageName: node linkType: hard @@ -9437,7 +9437,7 @@ __metadata: gulp: "npm:5.0.1" gulp-brotli: "npm:3.0.0" gulp-json-transform: "npm:0.5.0" - gulp-rename: "npm:2.0.0" + gulp-rename: "npm:2.1.0" gulp-zopfli-green: "npm:6.0.2" hls.js: "npm:1.6.5" home-assistant-js-websocket: "npm:9.5.0" From b35a1fc9e07d8350151df6944cceb23d0e581a65 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 20:54:40 +0300 Subject: [PATCH 162/216] Update dependency @babel/core to v7.27.7 (#25992) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 56 ++++++++++++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index e74e17356d..9c3dc8842d 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "xss": "1.0.15" }, "devDependencies": { - "@babel/core": "7.27.4", + "@babel/core": "7.27.7", "@babel/helper-define-polyfill-provider": "0.6.4", "@babel/plugin-transform-runtime": "7.27.4", "@babel/preset-env": "7.27.2", diff --git a/yarn.lock b/yarn.lock index 71448f7bf5..41c6f90bbd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -70,30 +70,30 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:7.27.4, @babel/core@npm:^7.24.4": - version: 7.27.4 - resolution: "@babel/core@npm:7.27.4" +"@babel/core@npm:7.27.7, @babel/core@npm:^7.24.4": + version: 7.27.7 + resolution: "@babel/core@npm:7.27.7" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.27.1" - "@babel/generator": "npm:^7.27.3" + "@babel/generator": "npm:^7.27.5" "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-module-transforms": "npm:^7.27.3" - "@babel/helpers": "npm:^7.27.4" - "@babel/parser": "npm:^7.27.4" + "@babel/helpers": "npm:^7.27.6" + "@babel/parser": "npm:^7.27.7" "@babel/template": "npm:^7.27.2" - "@babel/traverse": "npm:^7.27.4" - "@babel/types": "npm:^7.27.3" + "@babel/traverse": "npm:^7.27.7" + "@babel/types": "npm:^7.27.7" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10/28c01186d5f2599e41f92c94fd14a02cfdcf4b74429b4028a8d16e45c1b08d3924c4275e56412f30fcd2664e5ddc2200f1c06cee8bffff4bba628ff1f20c6e70 + checksum: 10/3503d575ebbf6e66d43d17bbf14c7f93466e8f44ba6f566722747ae887d6c3890ecf64447a3bae8e431ea96907180ac8618b5452d85d9951f571116122b7f66d languageName: node linkType: hard -"@babel/generator@npm:^7.27.3": +"@babel/generator@npm:^7.27.5": version: 7.27.5 resolution: "@babel/generator@npm:7.27.5" dependencies: @@ -290,7 +290,7 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.27.4": +"@babel/helpers@npm:^7.27.6": version: 7.27.6 resolution: "@babel/helpers@npm:7.27.6" dependencies: @@ -300,14 +300,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.4, @babel/parser@npm:^7.27.5": - version: 7.27.5 - resolution: "@babel/parser@npm:7.27.5" +"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.5, @babel/parser@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/parser@npm:7.27.7" dependencies: - "@babel/types": "npm:^7.27.3" + "@babel/types": "npm:^7.27.7" bin: parser: ./bin/babel-parser.js - checksum: 10/0ad671be7994dba7d31ec771bd70ea5090aa34faf73e93b1b072e3c0a704ab69f4a7a68ebfb9d6a7fa455e0aa03dfa65619c4df6bae1cf327cba925b1d233fc4 + checksum: 10/ed25ccfc709e77b94afebfa8377cca2ee5d0750162a6b4e7eb7b679ccdf307d1a015dee58d94afe726ed6d278a83aa348cb3a47717222ac4c3650d077f6ca4fd languageName: node linkType: hard @@ -1126,28 +1126,28 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.27.4": - version: 7.27.4 - resolution: "@babel/traverse@npm:7.27.4" +"@babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/traverse@npm:7.27.7" dependencies: "@babel/code-frame": "npm:^7.27.1" - "@babel/generator": "npm:^7.27.3" - "@babel/parser": "npm:^7.27.4" + "@babel/generator": "npm:^7.27.5" + "@babel/parser": "npm:^7.27.7" "@babel/template": "npm:^7.27.2" - "@babel/types": "npm:^7.27.3" + "@babel/types": "npm:^7.27.7" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 10/4debb80b9068a46e188e478272f3b6820e16d17e2651e82d0a0457176b0c3b2489994f0a0d6e8941ee90218b0a8a69fe52ba350c1aa66eb4c72570d6b2405f91 + checksum: 10/10b83c362b5c2758dbbf308c3144fa0fdcc98c8f107c2b7637e2c3c975f8b4e77a18e4b5854200f5ca3749ec3bcabd57bb9831ae8455f0701cabc6366983f379 languageName: node linkType: hard -"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6, @babel/types@npm:^7.4.4": - version: 7.27.6 - resolution: "@babel/types@npm:7.27.6" +"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6, @babel/types@npm:^7.27.7, @babel/types@npm:^7.4.4": + version: 7.27.7 + resolution: "@babel/types@npm:7.27.7" dependencies: "@babel/helper-string-parser": "npm:^7.27.1" "@babel/helper-validator-identifier": "npm:^7.27.1" - checksum: 10/174741c667775680628a09117828bbeffb35ea543f59bf80649d0d60672f7815a0740ddece3cca87516199033a039166a6936434131fce2b6a820227e64f91ae + checksum: 10/39e9f05527ef0771dfb6220213a9ef2ca35c2b6d531e3310c8ffafb53aa50362e809f75af8feb28bd6abb874a00c02b05ac00e3063ee239db5c6f1653eab19c5 languageName: node linkType: hard @@ -9303,7 +9303,7 @@ __metadata: version: 0.0.0-use.local resolution: "home-assistant-frontend@workspace:." dependencies: - "@babel/core": "npm:7.27.4" + "@babel/core": "npm:7.27.7" "@babel/helper-define-polyfill-provider": "npm:0.6.4" "@babel/plugin-transform-runtime": "npm:7.27.4" "@babel/preset-env": "npm:7.27.2" From e91d907e569caff5e723930b1da561eb7309b211 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 08:26:28 +0200 Subject: [PATCH 163/216] Update dependency prettier to v3.6.2 (#25996) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9c3dc8842d..2603beea0c 100644 --- a/package.json +++ b/package.json @@ -210,7 +210,7 @@ "lodash.template": "4.5.0", "map-stream": "0.0.7", "pinst": "3.0.0", - "prettier": "3.6.1", + "prettier": "3.6.2", "rspack-manifest-plugin": "5.0.3", "serve": "14.2.4", "sinon": "21.0.0", diff --git a/yarn.lock b/yarn.lock index 41c6f90bbd..8dea4ab8d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9464,7 +9464,7 @@ __metadata: node-vibrant: "npm:4.0.3" object-hash: "npm:3.0.0" pinst: "npm:3.0.0" - prettier: "npm:3.6.1" + prettier: "npm:3.6.2" punycode: "npm:2.3.1" qr-scanner: "npm:1.4.2" qrcode: "npm:1.5.4" @@ -12303,12 +12303,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.6.1": - version: 3.6.1 - resolution: "prettier@npm:3.6.1" +"prettier@npm:3.6.2": + version: 3.6.2 + resolution: "prettier@npm:3.6.2" bin: prettier: bin/prettier.cjs - checksum: 10/e2c4b47bf1bda4f932143e52e0f57239dd60cfef08296b56d4073426e2d310edc353a7daa5285a546bd3c1619c76894759cc98d82d4ac710f72a711ae2899bcc + checksum: 10/1213691706bcef1371d16ef72773c8111106c3533b660b1cc8ec158bd109cdf1462804125f87f981f23c4a3dba053b6efafda30ab0114cc5b4a725606bb9ff26 languageName: node linkType: hard From ad65600d11ec9cb42d71b3caafc82945bab59552 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:15:16 +0300 Subject: [PATCH 164/216] Update dependency marked to v16 (#25997) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 2603beea0c..ccdbf4f624 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "lit": "3.3.0", "lit-html": "3.3.0", "luxon": "3.6.1", - "marked": "15.0.12", + "marked": "16.0.0", "memoize-one": "6.0.0", "node-vibrant": "4.0.3", "object-hash": "3.0.0", diff --git a/yarn.lock b/yarn.lock index 8dea4ab8d7..5795ce5c0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9459,7 +9459,7 @@ __metadata: lodash.template: "npm:4.5.0" luxon: "npm:3.6.1" map-stream: "npm:0.0.7" - marked: "npm:15.0.12" + marked: "npm:16.0.0" memoize-one: "npm:6.0.0" node-vibrant: "npm:4.0.3" object-hash: "npm:3.0.0" @@ -11149,12 +11149,12 @@ __metadata: languageName: node linkType: hard -"marked@npm:15.0.12": - version: 15.0.12 - resolution: "marked@npm:15.0.12" +"marked@npm:16.0.0": + version: 16.0.0 + resolution: "marked@npm:16.0.0" bin: marked: bin/marked.js - checksum: 10/deeb619405c0c46af00c99b18b3365450abeb309104b24e3658f46142344f6b7c4117608c3b5834084d8738e92f81240c19f596e6ee369260f96e52b3457eaee + checksum: 10/aed6501ff5d49def83f0c79fab698666338a555cae32f83ba52369c9a04aa8be4fa4c908367d12f81f7d1c1a83d89ffe47aa1e7b023da308591e03ea82d5ca6d languageName: node linkType: hard From 7ad07e4c55b7447551f2871958c31d6676ab9751 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:16:59 +0200 Subject: [PATCH 165/216] Fix fullscreen yaml editor (transparency background) (#25989) Fix fullscreen editor (transparency background) --- src/components/ha-code-editor.ts | 6 +++--- src/resources/codemirror.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index be3eb9d283..5cb78c7193 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -559,9 +559,9 @@ export class HaCodeEditor extends ReactiveElement { right: 8px; z-index: 1; color: var(--secondary-text-color); - background-color: var(--card-background-color); + background-color: var(--secondary-background-color); border-radius: 50%; - opacity: 0.6; + opacity: 0.9; transition: opacity 0.2s; --mdc-icon-button-size: 32px; --mdc-icon-size: 18px; @@ -591,7 +591,7 @@ export class HaCodeEditor extends ReactiveElement { z-index: 9999 !important; background-color: var( --code-editor-background-color, - var(--mdc-text-field-fill-color, whitesmoke) + var(--card-background-color) ) !important; margin: 0 !important; padding-top: var(--safe-area-inset-top) !important; diff --git a/src/resources/codemirror.ts b/src/resources/codemirror.ts index 0999859c22..a5b8de8bb9 100644 --- a/src/resources/codemirror.ts +++ b/src/resources/codemirror.ts @@ -51,7 +51,7 @@ export const haTheme = EditorView.theme({ "&": { color: "var(--primary-text-color)", backgroundColor: - "var(--code-editor-background-color, var(--mdc-text-field-fill-color, whitesmoke))", + "var(--code-editor-background-color, var(--card-background-color))", borderRadius: "var(--mdc-shape-small, 4px) var(--mdc-shape-small, 4px) 0px 0px", caretColor: "var(--secondary-text-color)", From 06b0f9fcafe14dbeb5d6ad86a0549649b990317f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:49:05 +0200 Subject: [PATCH 166/216] Update dependency @rsdoctor/rspack-plugin to v1.1.5 (#26000) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 100 +++++++++++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index ccdbf4f624..f045172a6b 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,7 @@ "@octokit/auth-oauth-device": "8.0.1", "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", - "@rsdoctor/rspack-plugin": "1.1.4", + "@rsdoctor/rspack-plugin": "1.1.5", "@rspack/cli": "1.4.0", "@rspack/core": "1.4.0", "@types/babel__plugin-transform-runtime": "7.9.5", diff --git a/yarn.lock b/yarn.lock index 5795ce5c0a..23efd7a008 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3804,22 +3804,22 @@ __metadata: languageName: node linkType: hard -"@rsdoctor/client@npm:1.1.4": - version: 1.1.4 - resolution: "@rsdoctor/client@npm:1.1.4" - checksum: 10/549dd4093f2c652522351aafa7be36e565b51c0f4c5fbabb2d58c79f74554399059f71cf543cb82cf9af77b195a80dbed4609c3a384f46f597b6bbaaa75433a2 +"@rsdoctor/client@npm:1.1.5": + version: 1.1.5 + resolution: "@rsdoctor/client@npm:1.1.5" + checksum: 10/fa157b12647a668d01452eba112f5c1cf338d6ca6b2b201a95f6275a42b6acde068fc70b7e0990961ab04ef77c2ae9278e37076bfa78c0c449b6ded8b716b06b languageName: node linkType: hard -"@rsdoctor/core@npm:1.1.4": - version: 1.1.4 - resolution: "@rsdoctor/core@npm:1.1.4" +"@rsdoctor/core@npm:1.1.5": + version: 1.1.5 + resolution: "@rsdoctor/core@npm:1.1.5" dependencies: "@rsbuild/plugin-check-syntax": "npm:1.3.0" - "@rsdoctor/graph": "npm:1.1.4" - "@rsdoctor/sdk": "npm:1.1.4" - "@rsdoctor/types": "npm:1.1.4" - "@rsdoctor/utils": "npm:1.1.4" + "@rsdoctor/graph": "npm:1.1.5" + "@rsdoctor/sdk": "npm:1.1.5" + "@rsdoctor/types": "npm:1.1.5" + "@rsdoctor/utils": "npm:1.1.5" axios: "npm:^1.10.0" browserslist-load-config: "npm:^1.0.0" enhanced-resolve: "npm:5.12.0" @@ -3830,50 +3830,50 @@ __metadata: semver: "npm:^7.7.2" source-map: "npm:^0.7.4" webpack-bundle-analyzer: "npm:^4.10.2" - checksum: 10/bbba4429dffe55ad7ec6df10aab5c2b895a6dd1d95b619a7e1e5f1d66f09127098618bce12aa959cc5e4bfcd670171aa7e472109deb47afce39814d69229bfe6 + checksum: 10/740a52256d027114d798d997450be502673d976f215ff03b41aafc422c71d62a2c9ff419332ed6865dc9e6fe37f7c4469e19ce571745417fa2b19060b70079a6 languageName: node linkType: hard -"@rsdoctor/graph@npm:1.1.4": - version: 1.1.4 - resolution: "@rsdoctor/graph@npm:1.1.4" +"@rsdoctor/graph@npm:1.1.5": + version: 1.1.5 + resolution: "@rsdoctor/graph@npm:1.1.5" dependencies: - "@rsdoctor/types": "npm:1.1.4" - "@rsdoctor/utils": "npm:1.1.4" + "@rsdoctor/types": "npm:1.1.5" + "@rsdoctor/utils": "npm:1.1.5" lodash.unionby: "npm:^4.8.0" socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" - checksum: 10/6f067e602d223a6743a3d858cf25a9967d8ccd572980a0841a6fb00789110c7ddca64548213dfb7848158579b6c16f31a700f93fecb90f0d6d399951e045bf77 + checksum: 10/ce6eec3d82ea6717d9d2c2f9e6af9e9e75876814d85e50bbfc5747a4842f275bb10eea035ffbf60c2de1d1273f505a18e7d4111df8acfb0a386eedcf1d65cd1b languageName: node linkType: hard -"@rsdoctor/rspack-plugin@npm:1.1.4": - version: 1.1.4 - resolution: "@rsdoctor/rspack-plugin@npm:1.1.4" +"@rsdoctor/rspack-plugin@npm:1.1.5": + version: 1.1.5 + resolution: "@rsdoctor/rspack-plugin@npm:1.1.5" dependencies: - "@rsdoctor/core": "npm:1.1.4" - "@rsdoctor/graph": "npm:1.1.4" - "@rsdoctor/sdk": "npm:1.1.4" - "@rsdoctor/types": "npm:1.1.4" - "@rsdoctor/utils": "npm:1.1.4" + "@rsdoctor/core": "npm:1.1.5" + "@rsdoctor/graph": "npm:1.1.5" + "@rsdoctor/sdk": "npm:1.1.5" + "@rsdoctor/types": "npm:1.1.5" + "@rsdoctor/utils": "npm:1.1.5" lodash: "npm:^4.17.21" peerDependencies: "@rspack/core": "*" peerDependenciesMeta: "@rspack/core": optional: true - checksum: 10/c6a58e6321674c87a3f9e56cd57c21a9342139c43cae918730f6cf375dcfbd4cad3f458add3604169fcbee96e623eb5bc4c7d661c720a653d41968d860b7dc25 + checksum: 10/8265504174d21d3682ce7a015dfcaa11f460b6c3723118c380895014a69b08246b771cbb67a4102797a43f05398b760e82512939a9c1963ab7bbf376c3efd6e0 languageName: node linkType: hard -"@rsdoctor/sdk@npm:1.1.4": - version: 1.1.4 - resolution: "@rsdoctor/sdk@npm:1.1.4" +"@rsdoctor/sdk@npm:1.1.5": + version: 1.1.5 + resolution: "@rsdoctor/sdk@npm:1.1.5" dependencies: - "@rsdoctor/client": "npm:1.1.4" - "@rsdoctor/graph": "npm:1.1.4" - "@rsdoctor/types": "npm:1.1.4" - "@rsdoctor/utils": "npm:1.1.4" + "@rsdoctor/client": "npm:1.1.5" + "@rsdoctor/graph": "npm:1.1.5" + "@rsdoctor/types": "npm:1.1.5" + "@rsdoctor/utils": "npm:1.1.5" "@types/fs-extra": "npm:^11.0.4" body-parser: "npm:1.20.3" cors: "npm:2.8.5" @@ -3886,13 +3886,13 @@ __metadata: socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" tapable: "npm:2.2.2" - checksum: 10/856df8c95e5329c52d90aa7a311e2b34252e6268a13476e887f2939d67dbbacb8aadbbe2bbaba46c43009345c0eaf490afc1e57f0570aa0af370ba6a3362098a + checksum: 10/bf14b497961c5bc79cea4f3242829611a36907e0c215bc857dad2c0db9efe389dc755987936ea45b41c7a229160a89ff689ab70021da3fb780a69e748354baab languageName: node linkType: hard -"@rsdoctor/types@npm:1.1.4": - version: 1.1.4 - resolution: "@rsdoctor/types@npm:1.1.4" +"@rsdoctor/types@npm:1.1.5": + version: 1.1.5 + resolution: "@rsdoctor/types@npm:1.1.5" dependencies: "@types/connect": "npm:3.4.38" "@types/estree": "npm:1.0.5" @@ -3906,16 +3906,16 @@ __metadata: optional: true webpack: optional: true - checksum: 10/f9d9c63b042aa9272a4b7d50500c699c3246a1c4937a6f0fb71b8d8911da71f08a472e2b3bd112b47248e2d05ce58af7326d07ab57e66ea7b07aa7b1d93f3761 + checksum: 10/5791f053503c1cf811938d3273fcc1991fc7d6b387af7a3309e690be4bec102a0603cf24775ace70420671be9a107b2cca98904f045d98f808354f64413d6bdd languageName: node linkType: hard -"@rsdoctor/utils@npm:1.1.4": - version: 1.1.4 - resolution: "@rsdoctor/utils@npm:1.1.4" +"@rsdoctor/utils@npm:1.1.5": + version: 1.1.5 + resolution: "@rsdoctor/utils@npm:1.1.5" dependencies: "@babel/code-frame": "npm:7.26.2" - "@rsdoctor/types": "npm:1.1.4" + "@rsdoctor/types": "npm:1.1.5" "@types/estree": "npm:1.0.5" acorn: "npm:^8.10.0" acorn-import-attributes: "npm:^1.9.5" @@ -3929,9 +3929,9 @@ __metadata: json-stream-stringify: "npm:3.0.1" lines-and-columns: "npm:2.0.4" picocolors: "npm:^1.1.1" - rslog: "npm:^1.2.4" + rslog: "npm:^1.2.7" strip-ansi: "npm:^6.0.1" - checksum: 10/355e48cf9973d66b38666192af333a9c3f0abbe06c3a9872830070a46aacabbcf48321f91c4095832cbfea263c522f2c0054511eca396a50272d30c14302787e + checksum: 10/4b128c455bf2246b48243517e752fb01fa9495df6a7a98fb595c3cc369384f71513f0ec50ee83c8703c95c40f303839b4fe533bc86f3dd65aee71c2c3ad0beb0 languageName: node linkType: hard @@ -9370,7 +9370,7 @@ __metadata: "@octokit/plugin-retry": "npm:8.0.1" "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" - "@rsdoctor/rspack-plugin": "npm:1.1.4" + "@rsdoctor/rspack-plugin": "npm:1.1.5" "@rspack/cli": "npm:1.4.0" "@rspack/core": "npm:1.4.0" "@shoelace-style/shoelace": "npm:2.20.1" @@ -12954,10 +12954,10 @@ __metadata: languageName: node linkType: hard -"rslog@npm:^1.2.4": - version: 1.2.7 - resolution: "rslog@npm:1.2.7" - checksum: 10/941fc2430c7cee95befe3ecb493aae51321cf6a7e0d58b31be751860fbe321eb198fa48a1d880ab0b58e9c57f36f0cdd98550d72b8405282ef74a4193e330521 +"rslog@npm:^1.2.7": + version: 1.2.9 + resolution: "rslog@npm:1.2.9" + checksum: 10/f8c1d890049671aa73fa9c5682befd756c76fcfe885841ca3d6e1167edf1798a890dd57fb94a1493de7c7d28b41a9e42119dd9a483495acaa666ac04f149ee86 languageName: node linkType: hard From f3380891486c01f2a75c83524578b5aeed85f114 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:59:15 +0200 Subject: [PATCH 167/216] Pass area control service calls through hass (#25986) Connection logging --- src/common/entity/group_entities.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/entity/group_entities.ts b/src/common/entity/group_entities.ts index 32f9634af5..26237a699c 100644 --- a/src/common/entity/group_entities.ts +++ b/src/common/entity/group_entities.ts @@ -1,4 +1,4 @@ -import { callService, type HassEntity } from "home-assistant-js-websocket"; +import type { HassEntity } from "home-assistant-js-websocket"; import { computeStateDomain } from "./compute_state_domain"; import { isUnavailableState, UNAVAILABLE } from "../../data/entity"; import type { HomeAssistant } from "../../types"; @@ -62,7 +62,7 @@ export const toggleGroupEntities = ( const entitiesIds = states.map((stateObj) => stateObj.entity_id); - callService(hass.connection, domain, service, { + hass.callService(domain, service, { entity_id: entitiesIds, }); }; From 869d10ca3f966292f8c5be8d9ac7088588351a6a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:22:09 +0200 Subject: [PATCH 168/216] Update CodeMirror (#26003) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 ++-- yarn.lock | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index f045172a6b..c66127752d 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,11 @@ "@braintree/sanitize-url": "7.1.1", "@codemirror/autocomplete": "6.18.6", "@codemirror/commands": "6.8.1", - "@codemirror/language": "6.11.1", + "@codemirror/language": "6.11.2", "@codemirror/legacy-modes": "6.5.1", "@codemirror/search": "6.5.11", "@codemirror/state": "6.5.2", - "@codemirror/view": "6.37.2", + "@codemirror/view": "6.38.0", "@egjs/hammerjs": "2.0.17", "@formatjs/intl-datetimeformat": "6.18.0", "@formatjs/intl-displaynames": "6.8.11", diff --git a/yarn.lock b/yarn.lock index 23efd7a008..e05a1af6f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1200,9 +1200,9 @@ __metadata: languageName: node linkType: hard -"@codemirror/language@npm:6.11.1, @codemirror/language@npm:^6.0.0": - version: 6.11.1 - resolution: "@codemirror/language@npm:6.11.1" +"@codemirror/language@npm:6.11.2, @codemirror/language@npm:^6.0.0": + version: 6.11.2 + resolution: "@codemirror/language@npm:6.11.2" dependencies: "@codemirror/state": "npm:^6.0.0" "@codemirror/view": "npm:^6.23.0" @@ -1210,7 +1210,7 @@ __metadata: "@lezer/highlight": "npm:^1.0.0" "@lezer/lr": "npm:^1.0.0" style-mod: "npm:^4.0.0" - checksum: 10/024969113d61ccb5d497b75a8a9875d43e1bfc8466de2819bb5db23f01b200937366800a812e8d33eb5e34e3a5d2aa22d0ff8205f34a5e8aeb4cd1d221bcaa78 + checksum: 10/6ecccc48ad4390fca94525eefd0f4c904effad285e1d1de0db3764f08fd33299e5e453ab4d9ff8c33b4baeeb95f4a78660152ab64255fd19eccd31142410f6ed languageName: node linkType: hard @@ -1243,15 +1243,15 @@ __metadata: languageName: node linkType: hard -"@codemirror/view@npm:6.37.2, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0": - version: 6.37.2 - resolution: "@codemirror/view@npm:6.37.2" +"@codemirror/view@npm:6.38.0, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0": + version: 6.38.0 + resolution: "@codemirror/view@npm:6.38.0" dependencies: "@codemirror/state": "npm:^6.5.0" crelt: "npm:^1.0.6" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/410ef68c5bd3395c556f5a1298afd6a37eecd88b825435dc19ce7143b0eb5c4114486b444e56fd156371fcc329ad9425635c0703322384ff582a75a4d61727a7 + checksum: 10/30a2d103d0926a1677217d977578ae351a00e6c2777ea3eeadb24619f3477a8368668ccd51722dd3d05c84227b707b8938ce04283ad2ccb41bd00a00ef4bdbb0 languageName: node linkType: hard @@ -9312,11 +9312,11 @@ __metadata: "@bundle-stats/plugin-webpack-filter": "npm:4.20.2" "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/commands": "npm:6.8.1" - "@codemirror/language": "npm:6.11.1" + "@codemirror/language": "npm:6.11.2" "@codemirror/legacy-modes": "npm:6.5.1" "@codemirror/search": "npm:6.5.11" "@codemirror/state": "npm:6.5.2" - "@codemirror/view": "npm:6.37.2" + "@codemirror/view": "npm:6.38.0" "@egjs/hammerjs": "npm:2.0.17" "@formatjs/intl-datetimeformat": "npm:6.18.0" "@formatjs/intl-displaynames": "npm:6.8.11" From b7d1ce1c3750b92903a2d87f7f8cb588c4e0f8d9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:22:39 +0200 Subject: [PATCH 169/216] Update rspack monorepo to v1.4.1 (#26001) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 110 +++++++++++++++++++++++++-------------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index c66127752d..9be5ebd950 100644 --- a/package.json +++ b/package.json @@ -159,8 +159,8 @@ "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", "@rsdoctor/rspack-plugin": "1.1.5", - "@rspack/cli": "1.4.0", - "@rspack/core": "1.4.0", + "@rspack/cli": "1.4.1", + "@rspack/core": "1.4.1", "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.22", "@types/chromecast-caf-sender": "1.0.11", diff --git a/yarn.lock b/yarn.lock index e05a1af6f3..1bd63a32e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3935,92 +3935,92 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-darwin-arm64@npm:1.4.0" +"@rspack/binding-darwin-arm64@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-darwin-arm64@npm:1.4.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-darwin-x64@npm:1.4.0" +"@rspack/binding-darwin-x64@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-darwin-x64@npm:1.4.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.4.0" +"@rspack/binding-linux-arm64-gnu@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.4.1" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.4.0" +"@rspack/binding-linux-arm64-musl@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.4.1" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.4.0" +"@rspack/binding-linux-x64-gnu@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.4.1" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-linux-x64-musl@npm:1.4.0" +"@rspack/binding-linux-x64-musl@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-linux-x64-musl@npm:1.4.1" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-wasm32-wasi@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-wasm32-wasi@npm:1.4.0" +"@rspack/binding-wasm32-wasi@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-wasm32-wasi@npm:1.4.1" dependencies: "@napi-rs/wasm-runtime": "npm:^0.2.11" conditions: cpu=wasm32 languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.4.0" +"@rspack/binding-win32-arm64-msvc@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.4.1" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.4.0" +"@rspack/binding-win32-ia32-msvc@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.4.1" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.4.0" +"@rspack/binding-win32-x64-msvc@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.4.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/binding@npm:1.4.0" +"@rspack/binding@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/binding@npm:1.4.1" dependencies: - "@rspack/binding-darwin-arm64": "npm:1.4.0" - "@rspack/binding-darwin-x64": "npm:1.4.0" - "@rspack/binding-linux-arm64-gnu": "npm:1.4.0" - "@rspack/binding-linux-arm64-musl": "npm:1.4.0" - "@rspack/binding-linux-x64-gnu": "npm:1.4.0" - "@rspack/binding-linux-x64-musl": "npm:1.4.0" - "@rspack/binding-wasm32-wasi": "npm:1.4.0" - "@rspack/binding-win32-arm64-msvc": "npm:1.4.0" - "@rspack/binding-win32-ia32-msvc": "npm:1.4.0" - "@rspack/binding-win32-x64-msvc": "npm:1.4.0" + "@rspack/binding-darwin-arm64": "npm:1.4.1" + "@rspack/binding-darwin-x64": "npm:1.4.1" + "@rspack/binding-linux-arm64-gnu": "npm:1.4.1" + "@rspack/binding-linux-arm64-musl": "npm:1.4.1" + "@rspack/binding-linux-x64-gnu": "npm:1.4.1" + "@rspack/binding-linux-x64-musl": "npm:1.4.1" + "@rspack/binding-wasm32-wasi": "npm:1.4.1" + "@rspack/binding-win32-arm64-msvc": "npm:1.4.1" + "@rspack/binding-win32-ia32-msvc": "npm:1.4.1" + "@rspack/binding-win32-x64-msvc": "npm:1.4.1" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -4042,13 +4042,13 @@ __metadata: optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/7eeaa873f1d7305e271523d441f9f58ac05489d5dce9c4e435ff6a0ea060a37456cba7b485809dbdd797529603f1c2ee760e888cb18e022dc5f523116c85b8b0 + checksum: 10/565de02d4b5aabde5e53f56bb6515237d561308df45dd2c4406fd684a58c6a5b7928516af05e990c7e94ce67b48198285980f07393f79500e6ce8b283a549e00 languageName: node linkType: hard -"@rspack/cli@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/cli@npm:1.4.0" +"@rspack/cli@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/cli@npm:1.4.1" dependencies: "@discoveryjs/json-ext": "npm:^0.5.7" "@rspack/dev-server": "npm:~1.1.3" @@ -4062,23 +4062,23 @@ __metadata: "@rspack/core": ^1.0.0-alpha || ^1.x bin: rspack: bin/rspack.js - checksum: 10/9dd8f0aeea442177d5fbf3fd69a30810cae58d1a1c8e6cb7526b89e6c20dc573eada9ec3f9a7a9b122290b85c3d09eb707f5ac85c083147c92d425d1e89813b6 + checksum: 10/ab62d3f1c7a157c22968a3b620f7c52011ea96c6145895db529104679bb7032ab769841db3e4667f1d6f13b406faad9d07092f44ac607b8c9dfcbe6337a8859f languageName: node linkType: hard -"@rspack/core@npm:1.4.0": - version: 1.4.0 - resolution: "@rspack/core@npm:1.4.0" +"@rspack/core@npm:1.4.1": + version: 1.4.1 + resolution: "@rspack/core@npm:1.4.1" dependencies: "@module-federation/runtime-tools": "npm:0.15.0" - "@rspack/binding": "npm:1.4.0" + "@rspack/binding": "npm:1.4.1" "@rspack/lite-tapable": "npm:1.0.1" peerDependencies: "@swc/helpers": ">=0.5.1" peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/ac4000b8ed9d0ef605199bcb615ae07de8105f3bc69f29f82679d89a79a18c92a03b5271bff2a923afba5dd255202ebf98c4e980b7707338291c740baca7929c + checksum: 10/36d79846a7c7b26f6994feaa51e16ff5054e644011a70a0368315d472760ab4d3436fc900a6dee832085b5d4bb2b0d991919170e09fde599fe7525bf8dec8f9f languageName: node linkType: hard @@ -9371,8 +9371,8 @@ __metadata: "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" "@rsdoctor/rspack-plugin": "npm:1.1.5" - "@rspack/cli": "npm:1.4.0" - "@rspack/core": "npm:1.4.0" + "@rspack/cli": "npm:1.4.1" + "@rspack/core": "npm:1.4.1" "@shoelace-style/shoelace": "npm:2.20.1" "@swc/helpers": "npm:0.5.17" "@thomasloven/round-slider": "npm:0.6.0" From 89d9dd28932a7f7108a48db2980b200e43b10b0f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 30 Jun 2025 17:44:50 +0200 Subject: [PATCH 170/216] Improve device row in integration page (#26005) Improve device row in config entry page --- .../ha-config-entry-device-row.ts | 197 +++++++++--------- src/translations/en.json | 1 + 2 files changed, 104 insertions(+), 94 deletions(-) diff --git a/src/panels/config/integrations/ha-config-entry-device-row.ts b/src/panels/config/integrations/ha-config-entry-device-row.ts index f2654f61e3..6783240f46 100644 --- a/src/panels/config/integrations/ha-config-entry-device-row.ts +++ b/src/panels/config/integrations/ha-config-entry-device-row.ts @@ -1,9 +1,9 @@ import { - mdiCogOutline, mdiDelete, mdiDevices, mdiDotsVertical, mdiPencil, + mdiShapeOutline, mdiStopCircleOutline, mdiTransitConnectionVariant, } from "@mdi/js"; @@ -58,111 +58,118 @@ class HaConfigEntryDeviceRow extends LitElement { area ? area.name : undefined, ].filter(Boolean); - return html` - -
    ${computeDeviceNameDisplay(device, this.hass)}
    + return html` + +
    ${computeDeviceNameDisplay(device, this.hass)}
    ${supportingText.join(" • ")} ${supportingText.length && entities.length ? " • " : nothing} - ${ - entities.length - ? this.narrow - ? this.hass.localize( - "ui.panel.config.integrations.config_entry.entities", - { count: entities.length } - ) - : html`${this.hass.localize( - "ui.panel.config.integrations.config_entry.entities", - { count: entities.length } - )}` - : nothing - } - ${ - !this.narrow - ? html` - ` - : nothing - } - + ${!this.narrow + ? html` ` + : nothing}
    - ${ - !this.narrow - ? html`` - : nothing - } - - + ${!this.narrow + ? html`` + : nothing} + + - ${ - this.narrow - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.device.configure" - )} - ` - : nothing - } - - - - ${ - device.disabled_by && device.disabled_by !== "user" - ? this.hass.localize( - "ui.dialogs.device-registry-detail.enabled_cause", - { - type: this.hass.localize( - `ui.dialogs.device-registry-detail.type.${ - device.entry_type || "device" - }` - ), - cause: this.hass.localize( - `config_entry.disabled_by.${device.disabled_by}` - ), - } - ) - : device.disabled_by - ? this.hass.localize( - "ui.panel.config.integrations.config_entry.device.enable" - ) - : this.hass.localize( - "ui.panel.config.integrations.config_entry.device.disable" - ) - } - - - - ${ - this.entry.supports_remove_device - ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.device.edit" + )} + ` + : nothing} + ${entities.length + ? html` + - + ${this.hass.localize( - "ui.panel.config.integrations.config_entry.device.delete" + `ui.panel.config.integrations.config_entry.entities`, + { count: entities.length } )} - ` - : nothing - } + + + ` + : nothing} + + + + ${device.disabled_by && device.disabled_by !== "user" + ? this.hass.localize( + "ui.dialogs.device-registry-detail.enabled_cause", + { + type: this.hass.localize( + `ui.dialogs.device-registry-detail.type.${ + device.entry_type || "device" + }` + ), + cause: this.hass.localize( + `config_entry.disabled_by.${device.disabled_by}` + ), + } + ) + : device.disabled_by + ? this.hass.localize( + "ui.panel.config.integrations.config_entry.device.enable" + ) + : this.hass.localize( + "ui.panel.config.integrations.config_entry.device.disable" + )} + + ${this.entry.supports_remove_device + ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.device.delete" + )} + ` + : nothing}
    `; } @@ -170,7 +177,7 @@ class HaConfigEntryDeviceRow extends LitElement { private _getEntities = (): EntityRegistryEntry[] => this.entities?.filter((entity) => entity.device_id === this.device.id); - private _handleConfigureDevice(ev: MouseEvent) { + private _handleEditDevice(ev: MouseEvent) { ev.stopPropagation(); // Prevent triggering the click handler on the list item showDeviceRegistryDetailDialog(this, { device: this.device, @@ -295,6 +302,8 @@ class HaConfigEntryDeviceRow extends LitElement { } ha-md-list-item { --md-list-item-leading-space: 56px; + --md-ripple-hover-color: transparent; + --md-ripple-pressed-color: transparent; } .disabled { opacity: 0.5; diff --git a/src/translations/en.json b/src/translations/en.json index 44a10730c9..6b144bf04e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5383,6 +5383,7 @@ "confirm_disable_title": "Disable device?", "confirm_disable_message": "Are you sure you want to disable {name} and all of its entities?", "configure": "Configure device", + "edit": "Edit device", "delete": "Remove device" }, "devices": "{count} {count, plural,\n one {device}\n other {devices}\n}", From 8cc762d839ae16829bd335e4492287390064433a Mon Sep 17 00:00:00 2001 From: Kevin Lakotko Date: Mon, 30 Jun 2025 12:00:45 -0400 Subject: [PATCH 171/216] Fix use of numeric option for collator (#25917) * fix(string): use numeric option for collator * test: add natural sort comparison tests --- src/common/string/compare.ts | 5 ++-- test/common/string/sort.test.ts | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 test/common/string/sort.test.ts diff --git a/src/common/string/compare.ts b/src/common/string/compare.ts index 9d6aca76b8..8a8daa64df 100644 --- a/src/common/string/compare.ts +++ b/src/common/string/compare.ts @@ -2,12 +2,13 @@ import memoizeOne from "memoize-one"; import { isIPAddress } from "./is_ip_address"; const collator = memoizeOne( - (language: string | undefined) => new Intl.Collator(language) + (language: string | undefined) => + new Intl.Collator(language, { numeric: true }) ); const caseInsensitiveCollator = memoizeOne( (language: string | undefined) => - new Intl.Collator(language, { sensitivity: "accent" }) + new Intl.Collator(language, { sensitivity: "accent", numeric: true }) ); const fallbackStringCompare = (a: string, b: string) => { diff --git a/test/common/string/sort.test.ts b/test/common/string/sort.test.ts new file mode 100644 index 0000000000..3eafdadb67 --- /dev/null +++ b/test/common/string/sort.test.ts @@ -0,0 +1,51 @@ +import { assert, describe, it } from "vitest"; + +import { stringCompare } from "../../../src/common/string/compare"; + +describe("stringCompare", () => { + // Node only ships with English support for `Intl`, so we cannot test for other language collators. + it("Ensure natural order reutrned when numeric value is included", () => { + assert.strictEqual(stringCompare("Helper 2", "Helper 10"), -1); + }); + + it("Ensure prefixed numeric value is sorted naturally", () => { + assert.strictEqual(stringCompare("2 Helper", "10 Helper"), -1); + }); + + it("Ensure order has reversed alphabet is sorted", () => { + const reverseAlphabet = [ + "z", + "y", + "x", + "w", + "v", + "u", + "t", + "d", + "c", + "b", + "a", + ]; + assert.deepStrictEqual( + [...reverseAlphabet].sort(stringCompare), + [...reverseAlphabet].reverse() + ); + }); + + it("Ensure natural order when using numbers", () => { + const testArray = [ + "Helper 1", + "Helper 10", + "Helper 2", + "Helper 3", + "Helper 4", + ]; + assert.deepStrictEqual([...testArray].sort(stringCompare), [ + "Helper 1", + "Helper 2", + "Helper 3", + "Helper 4", + "Helper 10", + ]); + }); +}); From 0fbd4305944924157eee02a11865401367fee81c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 30 Jun 2025 18:09:42 +0200 Subject: [PATCH 172/216] Allow to re-order floors in areas dashboard (#26002) * Allow to re-order floors in areas dashboard * Move drag handle to right * Improve typings * Only show drag handle if there is at least 2 floors --- .../ha-areas-floors-display-editor.ts | 208 +++++++++++------- src/components/ha-items-display-editor.ts | 38 ++-- .../areas/areas-dashboard-strategy.ts | 4 + .../areas/areas-overview-view-strategy.ts | 20 +- .../hui-areas-dashboard-strategy-editor.ts | 19 +- .../areas/helpers/areas-strategy-helper.ts | 25 ++- src/translations/en.json | 2 +- 7 files changed, 199 insertions(+), 117 deletions(-) diff --git a/src/components/ha-areas-floors-display-editor.ts b/src/components/ha-areas-floors-display-editor.ts index cd56b94ffc..5b372f932f 100644 --- a/src/components/ha-areas-floors-display-editor.ts +++ b/src/components/ha-areas-floors-display-editor.ts @@ -1,14 +1,15 @@ -import { mdiTextureBox } from "@mdi/js"; +import { mdiDrag, mdiTextureBox } from "@mdi/js"; import type { TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; +import { repeat } from "lit/directives/repeat"; import memoizeOne from "memoize-one"; import { fireEvent } from "../common/dom/fire_event"; import { computeFloorName } from "../common/entity/compute_floor_name"; import { getAreaContext } from "../common/entity/context/get_area_context"; -import { stringCompare } from "../common/string/compare"; import { areaCompare } from "../data/area_registry"; import type { FloorRegistryEntry } from "../data/floor_registry"; +import { getFloors } from "../panels/lovelace/strategies/areas/helpers/areas-strategy-helper"; import type { HomeAssistant } from "../types"; import "./ha-expansion-panel"; import "./ha-floor-icon"; @@ -17,9 +18,14 @@ import type { DisplayItem, DisplayValue } from "./ha-items-display-editor"; import "./ha-svg-icon"; import "./ha-textfield"; -export interface AreasDisplayValue { - hidden?: string[]; - order?: string[]; +export interface AreasFloorsDisplayValue { + areas_display?: { + hidden?: string[]; + order?: string[]; + }; + floors_display?: { + order?: string[]; + }; } const UNASSIGNED_FLOOR = "__unassigned__"; @@ -30,12 +36,10 @@ export class HaAreasFloorsDisplayEditor extends LitElement { @property() public label?: string; - @property({ attribute: false }) public value?: AreasDisplayValue; + @property({ attribute: false }) public value?: AreasFloorsDisplayValue; @property() public helper?: string; - @property({ type: Boolean }) public expanded = false; - @property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public required = false; @@ -44,55 +48,78 @@ export class HaAreasFloorsDisplayEditor extends LitElement { public showNavigationButton = false; protected render(): TemplateResult { - const groupedItems = this._groupedItems(this.hass.areas, this.hass.floors); + const groupedAreasItems = this._groupedAreasItems( + this.hass.areas, + this.hass.floors + ); - const filteredFloors = this._sortedFloors(this.hass.floors).filter( + const filteredFloors = this._sortedFloors( + this.hass.floors, + this.value?.floors_display?.order + ).filter( (floor) => // Only include floors that have areas assigned to them - groupedItems[floor.floor_id]?.length > 0 + groupedAreasItems[floor.floor_id]?.length > 0 ); const value: DisplayValue = { - order: this.value?.order ?? [], - hidden: this.value?.hidden ?? [], + order: this.value?.areas_display?.order ?? [], + hidden: this.value?.areas_display?.hidden ?? [], }; + const canReorderFloors = + filteredFloors.filter((floor) => floor.floor_id !== UNASSIGNED_FLOOR) + .length > 1; + return html` - ${this.label}` : nothing} + - - ${filteredFloors.map((floor, _, array) => { - const noFloors = - array.length === 1 && floor.floor_id === UNASSIGNED_FLOOR; - return html` -
    - ${noFloors - ? nothing - : html`
    - -

    ${computeFloorName(floor)}

    -
    `} -
    +
    + ${repeat( + filteredFloors, + (floor) => floor.floor_id, + (floor: FloorRegistryEntry) => html` + + + ${floor.floor_id === UNASSIGNED_FLOOR || !canReorderFloors + ? nothing + : html` + + `} -
    -
    - `; - })} - + + ` + )} +
    +
    `; } - private _groupedItems = memoizeOne( + private _groupedAreasItems = memoizeOne( ( hassAreas: HomeAssistant["areas"], // update items if floors change @@ -116,7 +143,6 @@ export class HaAreasFloorsDisplayEditor extends LitElement { label: area.name, icon: area.icon ?? undefined, iconPath: mdiTextureBox, - description: floor?.name, }); return acc; @@ -128,18 +154,17 @@ export class HaAreasFloorsDisplayEditor extends LitElement { ); private _sortedFloors = memoizeOne( - (hassFloors: HomeAssistant["floors"]): FloorRegistryEntry[] => { - const floors = Object.values(hassFloors).sort((floorA, floorB) => { - if (floorA.level !== floorB.level) { - return (floorA.level ?? 0) - (floorB.level ?? 0); - } - return stringCompare(floorA.name, floorB.name); - }); + ( + hassFloors: HomeAssistant["floors"], + order: string[] | undefined + ): FloorRegistryEntry[] => { + const floors = getFloors(hassFloors, order); + const noFloors = floors.length === 0; floors.push({ floor_id: UNASSIGNED_FLOOR, - name: this.hass.localize( - "ui.panel.lovelace.strategy.areas.others_areas" - ), + name: noFloors + ? this.hass.localize("ui.panel.lovelace.strategy.areas.areas") + : this.hass.localize("ui.panel.lovelace.strategy.areas.other_areas"), icon: null, level: null, aliases: [], @@ -150,17 +175,43 @@ export class HaAreasFloorsDisplayEditor extends LitElement { } ); - private async _areaDisplayChanged(ev) { + private _floorMoved(ev: CustomEvent) { ev.stopPropagation(); - const value = ev.detail.value as DisplayValue; - const currentFloorId = ev.currentTarget.floorId; + const newIndex = ev.detail.newIndex; + const oldIndex = ev.detail.oldIndex; + const floorIds = this._sortedFloors( + this.hass.floors, + this.value?.floors_display?.order + ).map((floor) => floor.floor_id); + const newOrder = [...floorIds]; + const movedFloorId = newOrder.splice(oldIndex, 1)[0]; + newOrder.splice(newIndex, 0, movedFloorId); + const newValue: AreasFloorsDisplayValue = { + areas_display: this.value?.areas_display, + floors_display: { + order: newOrder, + }, + }; + if (newValue.floors_display?.order?.length === 0) { + delete newValue.floors_display.order; + } + fireEvent(this, "value-changed", { value: newValue }); + } - const floorIds = this._sortedFloors(this.hass.floors).map( - (floor) => floor.floor_id - ); + private async _areaDisplayChanged(ev: CustomEvent<{ value: DisplayValue }>) { + ev.stopPropagation(); + const value = ev.detail.value; + const currentFloorId = (ev.currentTarget as any).floorId; - const oldHidden = this.value?.hidden ?? []; - const oldOrder = this.value?.order ?? []; + const floorIds = this._sortedFloors( + this.hass.floors, + this.value?.floors_display?.order + ).map((floor) => floor.floor_id); + + const oldAreaDisplay = this.value?.areas_display ?? {}; + + const oldHidden = oldAreaDisplay?.hidden ?? []; + const oldOrder = oldAreaDisplay?.order ?? []; const newHidden: string[] = []; const newOrder: string[] = []; @@ -187,37 +238,27 @@ export class HaAreasFloorsDisplayEditor extends LitElement { } } - const newValue: AreasDisplayValue = { - hidden: newHidden, - order: newOrder, + const newValue: AreasFloorsDisplayValue = { + areas_display: { + hidden: newHidden, + order: newOrder, + }, + floors_display: this.value?.floors_display, }; - if (newValue.hidden?.length === 0) { - delete newValue.hidden; + if (newValue.areas_display?.hidden?.length === 0) { + delete newValue.areas_display.hidden; } - if (newValue.order?.length === 0) { - delete newValue.order; + if (newValue.areas_display?.order?.length === 0) { + delete newValue.areas_display.order; } - this.value = newValue; + if (newValue.floors_display?.order?.length === 0) { + delete newValue.floors_display.order; + } + fireEvent(this, "value-changed", { value: newValue }); } static styles = css` - .floor .header p { - margin: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - flex: 1; - } - .floor .header { - margin: 16px 0 8px 0; - padding: 0 8px; - display: flex; - flex-direction: row; - align-items: center; - gap: 8px; - } ha-expansion-panel { margin-bottom: 8px; --expansion-panel-summary-padding: 0 16px; @@ -225,6 +266,11 @@ export class HaAreasFloorsDisplayEditor extends LitElement { ha-expansion-panel [slot="leading-icon"] { margin-inline-end: 16px; } + label { + display: block; + font-weight: var(--ha-font-weight-bold); + margin-bottom: 8px; + } `; } diff --git a/src/components/ha-items-display-editor.ts b/src/components/ha-items-display-editor.ts index e87ecfaef0..91d55820bf 100644 --- a/src/components/ha-items-display-editor.ts +++ b/src/components/ha-items-display-editor.ts @@ -122,22 +122,6 @@ export class HaItemDisplayEditor extends LitElement { ${description ? html`${description}` : nothing} - ${isVisible && !disableSorting - ? html` - - ` - : html``} ${!showIcon ? nothing : icon @@ -162,6 +146,9 @@ export class HaItemDisplayEditor extends LitElement { ${this.actionsRenderer(item)} ` : nothing} + ${this.showNavigationButton + ? html`` + : nothing} - ${this.showNavigationButton - ? html` ` - : nothing} + ${isVisible && !disableSorting + ? html` + + ` + : html``} `; } diff --git a/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts b/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts index 9ec7bc9486..6154719ec6 100644 --- a/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-dashboard-strategy.ts @@ -22,6 +22,9 @@ export interface AreasDashboardStrategyConfig { hidden?: string[]; order?: string[]; }; + floors_display?: { + order?: string[]; + }; areas_options?: Record; } @@ -84,6 +87,7 @@ export class AreasDashboardStrategy extends ReactiveElement { type: "areas-overview", areas_display: config.areas_display, areas_options: config.areas_options, + floors_display: config.floors_display, } satisfies AreasViewStrategyConfig, }, ...areaViews, diff --git a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts index 9c47f38ddd..9d2d75e2d2 100644 --- a/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts +++ b/src/panels/lovelace/strategies/areas/areas-overview-view-strategy.ts @@ -1,6 +1,5 @@ import { ReactiveElement } from "lit"; import { customElement } from "lit/decorators"; -import { stringCompare } from "../../../../common/string/compare"; import { floorDefaultIcon } from "../../../../components/ha-floor-icon"; import type { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; @@ -9,7 +8,11 @@ import { getAreaControlEntities } from "../../card-features/hui-area-controls-ca import { AREA_CONTROLS, type AreaControl } from "../../card-features/types"; import type { AreaCardConfig, HeadingCardConfig } from "../../cards/types"; import type { EntitiesDisplay } from "./area-view-strategy"; -import { computeAreaPath, getAreas } from "./helpers/areas-strategy-helper"; +import { + computeAreaPath, + getAreas, + getFloors, +} from "./helpers/areas-strategy-helper"; const UNASSIGNED_FLOOR = "__unassigned__"; @@ -23,6 +26,9 @@ export interface AreasViewStrategyConfig { hidden?: string[]; order?: string[]; }; + floors_display?: { + order?: string[]; + }; areas_options?: Record; } @@ -38,19 +44,13 @@ export class AreasOverviewViewStrategy extends ReactiveElement { config.areas_display?.order ); - const floors = Object.values(hass.floors); - floors.sort((floorA, floorB) => { - if (floorA.level !== floorB.level) { - return (floorA.level ?? 0) - (floorB.level ?? 0); - } - return stringCompare(floorA.name, floorB.name); - }); + const floors = getFloors(hass.floors, config.floors_display?.order); const floorSections = [ ...floors, { floor_id: UNASSIGNED_FLOOR, - name: hass.localize("ui.panel.lovelace.strategy.areas.others_areas"), + name: hass.localize("ui.panel.lovelace.strategy.areas.other_areas"), level: null, icon: null, }, diff --git a/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts b/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts index 73e7d890bb..96448afc83 100644 --- a/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts +++ b/src/panels/lovelace/strategies/areas/editor/hui-areas-dashboard-strategy-editor.ts @@ -1,10 +1,12 @@ import { mdiThermometerWater } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/ha-areas-display-editor"; import type { AreasDisplayValue } from "../../../../../components/ha-areas-display-editor"; import "../../../../../components/ha-areas-floors-display-editor"; +import type { AreasFloorsDisplayValue } from "../../../../../components/ha-areas-floors-display-editor"; import "../../../../../components/ha-entities-display-editor"; import "../../../../../components/ha-icon"; import "../../../../../components/ha-icon-button"; @@ -126,7 +128,7 @@ export class HuiAreasDashboardStrategyEditor `; } - const value = this._config.areas_display; + const value = this._areasFloorsDisplayValue(this._config); return html` ({ + areas_display: config.areas_display, + floors_display: config.floors_display, + }) + ); + private _editArea(ev: Event): void { ev.stopPropagation(); const area = (ev.currentTarget! as any).area as AreaRegistryEntry; @@ -163,11 +172,11 @@ export class HuiAreasDashboardStrategyEditor this._area = ev.detail.value; } - private _areasDisplayChanged(ev: CustomEvent): void { - const value = ev.detail.value as AreasDisplayValue; + private _areasFloorsDisplayChanged(ev: CustomEvent): void { + const value = ev.detail.value as AreasFloorsDisplayValue; const newConfig: AreasDashboardStrategyConfig = { ...this._config!, - areas_display: value, + ...value, }; fireEvent(this, "config-changed", { config: newConfig }); diff --git a/src/panels/lovelace/strategies/areas/helpers/areas-strategy-helper.ts b/src/panels/lovelace/strategies/areas/helpers/areas-strategy-helper.ts index cbebee032b..79582d02fa 100644 --- a/src/panels/lovelace/strategies/areas/helpers/areas-strategy-helper.ts +++ b/src/panels/lovelace/strategies/areas/helpers/areas-strategy-helper.ts @@ -3,9 +3,13 @@ import { computeStateName } from "../../../../../common/entity/compute_state_nam import type { EntityFilterFunc } from "../../../../../common/entity/entity_filter"; import { generateEntityFilter } from "../../../../../common/entity/entity_filter"; import { stripPrefixFromEntityName } from "../../../../../common/entity/strip_prefix_from_entity_name"; -import { orderCompare } from "../../../../../common/string/compare"; +import { + orderCompare, + stringCompare, +} from "../../../../../common/string/compare"; import type { AreaRegistryEntry } from "../../../../../data/area_registry"; import { areaCompare } from "../../../../../data/area_registry"; +import type { FloorRegistryEntry } from "../../../../../data/floor_registry"; import type { LovelaceCardConfig } from "../../../../../data/lovelace/config/card"; import type { HomeAssistant } from "../../../../../types"; import { supportsAlarmModesCardFeature } from "../../../card-features/hui-alarm-modes-card-feature"; @@ -290,4 +294,23 @@ export const getAreas = ( return sortedAreas; }; +export const getFloors = ( + entries: HomeAssistant["floors"], + floorsOrder?: string[] +): FloorRegistryEntry[] => { + const floors = Object.values(entries); + const compare = orderCompare(floorsOrder || []); + + return floors.sort((floorA, floorB) => { + const order = compare(floorA.floor_id, floorB.floor_id); + if (order !== 0) { + return order; + } + if (floorA.level !== floorB.level) { + return (floorA.level ?? 0) - (floorB.level ?? 0); + } + return stringCompare(floorA.name, floorB.name); + }); +}; + export const computeAreaPath = (areaId: string): string => `areas-${areaId}`; diff --git a/src/translations/en.json b/src/translations/en.json index 6b144bf04e..948e4113cd 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6686,7 +6686,7 @@ "actions": "Actions", "others": "Others" }, - "others_areas": "Other areas", + "other_areas": "Other areas", "areas": "Areas" } }, From 49099223d35f22af2288ba2ebeb98c914639a030 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 30 Jun 2025 18:10:08 +0200 Subject: [PATCH 173/216] Do not display quality scale for custom integrations (#26006) --- src/panels/config/integrations/ha-config-integration-page.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 4b11f37d86..744b3fd181 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -432,7 +432,8 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { )}
    ` : nothing} - ${this._manifest?.quality_scale && + ${this._manifest?.is_built_in && + this._manifest.quality_scale && Object.keys(QUALITY_SCALE_MAP).includes( this._manifest.quality_scale ) From 948c858e781f8173d562480752f66cd7d6eb45c5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 30 Jun 2025 18:15:37 +0200 Subject: [PATCH 174/216] Fix object selector not displayed (#26007) --- src/components/ha-selector/ha-selector-object.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/ha-selector/ha-selector-object.ts b/src/components/ha-selector/ha-selector-object.ts index bca45148b3..626699c4fe 100644 --- a/src/components/ha-selector/ha-selector-object.ts +++ b/src/components/ha-selector/ha-selector-object.ts @@ -122,11 +122,7 @@ export class HaObjectSelector extends LitElement { } protected render() { - if (!this.selector.object) { - return nothing; - } - - if (this.selector.object.fields) { + if (this.selector.object?.fields) { if (this.selector.object.multiple) { const items = ensureArray(this.value ?? []); return html` From ad4f14ffafe1a8298556fcf90f1258f78615ca7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 08:31:30 +0200 Subject: [PATCH 175/216] Update dependency eslint to v9.30.0 (#26012) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 9be5ebd950..f1af6d8795 100644 --- a/package.json +++ b/package.json @@ -184,7 +184,7 @@ "babel-plugin-template-html-minifier": "4.1.0", "browserslist-useragent-regexp": "4.1.3", "del": "8.0.0", - "eslint": "9.29.0", + "eslint": "9.30.0", "eslint-config-airbnb-base": "15.0.0", "eslint-config-prettier": "10.1.5", "eslint-import-resolver-webpack": "0.13.10", diff --git a/yarn.lock b/yarn.lock index 1bd63a32e2..ddebeaaff7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1545,21 +1545,21 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.20.1": - version: 0.20.1 - resolution: "@eslint/config-array@npm:0.20.1" +"@eslint/config-array@npm:^0.21.0": + version: 0.21.0 + resolution: "@eslint/config-array@npm:0.21.0" dependencies: "@eslint/object-schema": "npm:^2.1.6" debug: "npm:^4.3.1" minimatch: "npm:^3.1.2" - checksum: 10/d72cc90f516c5730da5f37fa04aa8ba26ea0d92c7457ee77980902158f844f3483518272ccfe16f273c3313c3bfec8da713d4e51d3da49bdeccd34e919a2b903 + checksum: 10/f5a499e074ecf4b4a5efdca655418a12079d024b77d02fd35868eeb717c5bfdd8e32c6e8e1dd125330233a878026edda8062b13b4310169ba5bfee9623a67aa0 languageName: node linkType: hard -"@eslint/config-helpers@npm:^0.2.1": - version: 0.2.3 - resolution: "@eslint/config-helpers@npm:0.2.3" - checksum: 10/1f5082248f65555cc666942f7c991a2cfd6821758fb45338f43b28ea0f6b77d0c48b35097400d9b8fe1b4b10150085452e0b8f2d6d9ba17a84e16a6c7e4b341d +"@eslint/config-helpers@npm:^0.3.0": + version: 0.3.0 + resolution: "@eslint/config-helpers@npm:0.3.0" + checksum: 10/b4c188f28cb8b76d4f4b49566ec1cc9d561bc888ef66ad34587151a212ff168afcf163493c72033149181f947cb950c3cca1525d7486303aae4dfde3e5399573 languageName: node linkType: hard @@ -1598,10 +1598,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.29.0": - version: 9.29.0 - resolution: "@eslint/js@npm:9.29.0" - checksum: 10/7f7fd586b35bd08537dd65a9bda764f474350c36b4ccbdd342462d1a26be28f7ee0ebd0611dd4762b69829674336ba04c281b9658aeccb3e6ab1d0fec7e6d08c +"@eslint/js@npm:9.30.0": + version: 9.30.0 + resolution: "@eslint/js@npm:9.30.0" + checksum: 10/42e3d5a9cdd5a0842f3ed078e28f81ae1cf04bd2edfd09f43e6dc148bb2e99904f09090007eb6485afd82d837771890c5a8b9ceb1e8c4e256953df4b4aa97308 languageName: node linkType: hard @@ -8124,17 +8124,17 @@ __metadata: languageName: node linkType: hard -"eslint@npm:9.29.0": - version: 9.29.0 - resolution: "eslint@npm:9.29.0" +"eslint@npm:9.30.0": + version: 9.30.0 + resolution: "eslint@npm:9.30.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.20.1" - "@eslint/config-helpers": "npm:^0.2.1" + "@eslint/config-array": "npm:^0.21.0" + "@eslint/config-helpers": "npm:^0.3.0" "@eslint/core": "npm:^0.14.0" "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.29.0" + "@eslint/js": "npm:9.30.0" "@eslint/plugin-kit": "npm:^0.3.1" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" @@ -8170,7 +8170,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10/be0c8e123207c9d653fb75ddc610b85dfbf295a2bfa1cbecc78f191dcba9c421525b5befd5d499ce561eca607c9c33f455e4fff0b1c2d4202c2896dafe95094a + checksum: 10/74c11e6be5997f0de6542932795e997c1586f8f21cdeeda09c89c6c36879a9a593af84f1fd594bd8e22814c54ca0ad65513a0c91b0e8944efb51faed34b7d3b0 languageName: node linkType: hard @@ -9420,7 +9420,7 @@ __metadata: dialog-polyfill: "npm:0.5.6" echarts: "npm:5.6.0" element-internals-polyfill: "npm:3.0.2" - eslint: "npm:9.29.0" + eslint: "npm:9.30.0" eslint-config-airbnb-base: "npm:15.0.0" eslint-config-prettier: "npm:10.1.5" eslint-import-resolver-webpack: "npm:0.13.10" From 26d842f432d8cc56bfb9f2787243962a75be0085 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 08:34:48 +0200 Subject: [PATCH 176/216] Update dependency @babel/helper-define-polyfill-provider to v0.6.5 (#26008) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index f1af6d8795..55c48c413e 100644 --- a/package.json +++ b/package.json @@ -150,7 +150,7 @@ }, "devDependencies": { "@babel/core": "7.27.7", - "@babel/helper-define-polyfill-provider": "0.6.4", + "@babel/helper-define-polyfill-provider": "0.6.5", "@babel/plugin-transform-runtime": "7.27.4", "@babel/preset-env": "7.27.2", "@bundle-stats/plugin-webpack-filter": "4.20.2", diff --git a/yarn.lock b/yarn.lock index ddebeaaff7..37d4fae219 100644 --- a/yarn.lock +++ b/yarn.lock @@ -115,7 +115,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2": +"@babel/helper-compilation-targets@npm:^7.27.1, @babel/helper-compilation-targets@npm:^7.27.2": version: 7.27.2 resolution: "@babel/helper-compilation-targets@npm:7.27.2" dependencies: @@ -158,18 +158,18 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:0.6.4, @babel/helper-define-polyfill-provider@npm:^0.6.3, @babel/helper-define-polyfill-provider@npm:^0.6.4": - version: 0.6.4 - resolution: "@babel/helper-define-polyfill-provider@npm:0.6.4" +"@babel/helper-define-polyfill-provider@npm:0.6.5, @babel/helper-define-polyfill-provider@npm:^0.6.3, @babel/helper-define-polyfill-provider@npm:^0.6.4": + version: 0.6.5 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.5" dependencies: - "@babel/helper-compilation-targets": "npm:^7.22.6" - "@babel/helper-plugin-utils": "npm:^7.22.5" - debug: "npm:^4.1.1" + "@babel/helper-compilation-targets": "npm:^7.27.2" + "@babel/helper-plugin-utils": "npm:^7.27.1" + debug: "npm:^4.4.1" lodash.debounce: "npm:^4.0.8" - resolve: "npm:^1.14.2" + resolve: "npm:^1.22.10" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/dc2ebdd7bc880fff8cd09a5b0bd208e53d8b7ea9070f4b562dd3135ea6cd68ef80cf4a74f40424569a00c00eabbcdff67b2137a874c4f82f3530246dad267a3b + checksum: 10/0bdd2d9654d2f650c33976caa1a2afac2c23cf07e83856acdb482423c7bf4542c499ca0bdc723f2961bb36883501f09e9f4fe061ba81c07996daacfba82a6f62 languageName: node linkType: hard @@ -215,7 +215,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.27.1": +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-plugin-utils@npm:7.27.1" checksum: 10/96136c2428888e620e2ec493c25888f9ceb4a21099dcf3dd4508ea64b58cdedbd5a9fb6c7b352546de84d6c24edafe482318646932a22c449ebd16d16c22d864 @@ -9304,7 +9304,7 @@ __metadata: resolution: "home-assistant-frontend@workspace:." dependencies: "@babel/core": "npm:7.27.7" - "@babel/helper-define-polyfill-provider": "npm:0.6.4" + "@babel/helper-define-polyfill-provider": "npm:0.6.5" "@babel/plugin-transform-runtime": "npm:7.27.4" "@babel/preset-env": "npm:7.27.2" "@babel/runtime": "npm:7.27.6" @@ -12752,7 +12752,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4": +"resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.10, resolve@npm:^1.22.4": version: 1.22.10 resolution: "resolve@npm:1.22.10" dependencies: @@ -12778,7 +12778,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": +"resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.10#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin": version: 1.22.10 resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" dependencies: From 8644dd5271496c8f49d42421474a6e0a14fcfc1c Mon Sep 17 00:00:00 2001 From: Ezra Freedman <38084742+ezra-freedman@users.noreply.github.com> Date: Tue, 1 Jul 2025 02:48:13 -0400 Subject: [PATCH 177/216] Fix translation in the integration page for entities (#26009) add call to localize --- src/panels/config/integrations/ha-config-entry-row.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/panels/config/integrations/ha-config-entry-row.ts b/src/panels/config/integrations/ha-config-entry-row.ts index 41e843c0c4..37b1020096 100644 --- a/src/panels/config/integrations/ha-config-entry-row.ts +++ b/src/panels/config/integrations/ha-config-entry-row.ts @@ -154,7 +154,10 @@ class HaConfigEntryRow extends LitElement { statusLine.push( html`${entities.length} entities${this.hass.localize( + "ui.panel.config.integrations.config_entry.entities", + { count: entities.length } + )}` ); } From 4f97756f4e0a1be7ae587794937754ba1792d20f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 1 Jul 2025 14:22:55 +0200 Subject: [PATCH 178/216] Force narrow style for action, condition and trigger in blueprint (#26018) --- src/panels/config/blueprint/blueprint-generic-editor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts index 97d6ad7442..844b6f5abb 100644 --- a/src/panels/config/blueprint/blueprint-generic-editor.ts +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -173,6 +173,7 @@ export abstract class HaBlueprintGenericEditor extends LitElement { .content=${value?.description} > ${html` Date: Tue, 1 Jul 2025 14:32:54 +0200 Subject: [PATCH 179/216] Avoid selector to take to much space in action calls (#26014) --- src/components/ha-settings-row.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-settings-row.ts b/src/components/ha-settings-row.ts index c7994e8d57..fa946d81f8 100644 --- a/src/components/ha-settings-row.ts +++ b/src/components/ha-settings-row.ts @@ -89,6 +89,7 @@ export class HaSettingsRow extends LitElement { display: var(--settings-row-content-display, flex); justify-content: flex-end; flex: 1; + min-width: 0; padding: 16px 0; } .content ::slotted(*) { From 523eb9522fd50c7f687947debc0efa1c4f984c00 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 1 Jul 2025 14:33:36 +0200 Subject: [PATCH 180/216] Add dashboard title to strategy editor (#26015) --- .../dialogs/dialog-dashboard-strategy-editor.ts | 3 +++ .../dialogs/show-dialog-dashboard-strategy-editor.ts | 1 + src/panels/lovelace/hui-root.ts | 1 + 3 files changed, 5 insertions(+) diff --git a/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts index b9d5a40aed..c57efcc38e 100644 --- a/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts +++ b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts @@ -144,6 +144,9 @@ class DialogDashboardStrategyEditor extends LitElement { .path=${mdiClose} > ${title} + ${this._params.title + ? html`${this._params.title}` + : nothing} void; takeControl: () => void; deleteDashboard: () => Promise; diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 6f3d727d4d..1a6f09f7a7 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -782,6 +782,7 @@ class HUIRoot extends LitElement { showDashboardStrategyEditorDialog(this, { config: this.lovelace!.rawConfig, + title: this.panel ? getPanelTitle(this.hass, this.panel) : undefined, saveConfig: this.lovelace!.saveConfig, takeControl: () => { showSaveDialog(this, { From 1e59f9f4be23428e0c1813d29cf6e6244219d7bd Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 1 Jul 2025 14:34:19 +0200 Subject: [PATCH 181/216] Increase target area in tile card and area card (#26017) --- src/components/ha-control-button-group.ts | 1 - .../common/card-feature-styles.ts | 3 --- .../hui-area-controls-card-feature.ts | 23 +++++++++++++++++-- .../card-features/hui-card-feature.ts | 8 ++++++- .../card-features/hui-card-features.ts | 1 + 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/components/ha-control-button-group.ts b/src/components/ha-control-button-group.ts index fa9d0717c1..5c59d0801f 100644 --- a/src/components/ha-control-button-group.ts +++ b/src/components/ha-control-button-group.ts @@ -26,7 +26,6 @@ export class HaControlButtonGroup extends LitElement { .container { display: flex; flex-direction: row; - justify-content: var(--control-button-group-alignment, start); width: 100%; height: 100%; } diff --git a/src/panels/lovelace/card-features/common/card-feature-styles.ts b/src/panels/lovelace/card-features/common/card-feature-styles.ts index 15a6cebcd1..799570cc99 100644 --- a/src/panels/lovelace/card-features/common/card-feature-styles.ts +++ b/src/panels/lovelace/card-features/common/card-feature-styles.ts @@ -25,9 +25,6 @@ export const cardFeatureStyles = css` flex-basis: 20px; --control-button-padding: 0px; } - ha-control-button-group[no-stretch] > ha-control-button { - max-width: 48px; - } ha-control-button { --control-button-focus-color: var(--feature-color); } diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts index 441684a477..705f02855a 100644 --- a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -1,5 +1,6 @@ import type { HassEntity } from "home-assistant-js-websocket"; import { css, html, LitElement, nothing } from "lit"; +import { classMap } from "lit/directives/class-map"; import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; @@ -229,7 +230,11 @@ class HuiAreaControlsCardFeature } return html` - + ${displayControls.map((control) => { const button = AREA_CONTROLS_BUTTONS[control]; @@ -292,8 +297,22 @@ class HuiAreaControlsCardFeature return [ cardFeatureStyles, css` + :host { + pointer-events: none !important; + display: flex; + flex-direction: row; + justify-content: flex-end; + } ha-control-button-group { - --control-button-group-alignment: flex-end; + pointer-events: auto; + width: 100%; + } + ha-control-button-group.no-stretch { + width: auto; + max-width: 100%; + } + ha-control-button-group.no-stretch > ha-control-button { + width: 48px; } ha-control-button { --active-color: var(--state-active-color); diff --git a/src/panels/lovelace/card-features/hui-card-feature.ts b/src/panels/lovelace/card-features/hui-card-feature.ts index fa92cc89ae..9dae99634c 100644 --- a/src/panels/lovelace/card-features/hui-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-card-feature.ts @@ -1,4 +1,4 @@ -import { LitElement, html, nothing } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import type { HomeAssistant } from "../../../types"; import type { HuiErrorCard } from "../cards/hui-error-card"; @@ -56,6 +56,12 @@ export class HuiCardFeature extends LitElement { } return html`${element}`; } + + static styles = css` + :host > * { + pointer-events: auto; + } + `; } declare global { diff --git a/src/panels/lovelace/card-features/hui-card-features.ts b/src/panels/lovelace/card-features/hui-card-features.ts index 138e639657..21227fe0fa 100644 --- a/src/panels/lovelace/card-features/hui-card-features.ts +++ b/src/panels/lovelace/card-features/hui-card-features.ts @@ -46,6 +46,7 @@ export class HuiCardFeatures extends LitElement { --feature-height: 42px; --feature-border-radius: 12px; --feature-button-spacing: 12px; + pointer-events: none; position: relative; width: 100%; display: flex; From eb111d3c32de0d99321c6411d669691f3e100d22 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 1 Jul 2025 21:46:58 +0200 Subject: [PATCH 182/216] Add missing area helper (#26022) --- src/components/ha-area-picker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-area-picker.ts b/src/components/ha-area-picker.ts index 1f14568261..9b18057808 100644 --- a/src/components/ha-area-picker.ts +++ b/src/components/ha-area-picker.ts @@ -366,6 +366,7 @@ export class HaAreaPicker extends LitElement { .hass=${this.hass} .autofocus=${this.autofocus} .label=${this.label} + .helper=${this.helper} .notFoundLabel=${this.hass.localize( "ui.components.area-picker.no_match" )} From 50d705c9438d480bbcf8308272e0e7216abf22a3 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 1 Jul 2025 21:47:20 +0200 Subject: [PATCH 183/216] Add missing domain icon import in area controls (#26023) --- .../lovelace/card-features/hui-area-controls-card-feature.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts index 705f02855a..77fbbd50d0 100644 --- a/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-area-controls-card-feature.ts @@ -1,7 +1,7 @@ import type { HassEntity } from "home-assistant-js-websocket"; import { css, html, LitElement, nothing } from "lit"; -import { classMap } from "lit/directives/class-map"; import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { ensureArray } from "../../../common/array/ensure-array"; @@ -14,6 +14,7 @@ import { stateActive } from "../../../common/entity/state_active"; import { domainColorProperties } from "../../../common/entity/state_color"; import "../../../components/ha-control-button"; import "../../../components/ha-control-button-group"; +import "../../../components/ha-domain-icon"; import "../../../components/ha-svg-icon"; import type { AreaRegistryEntry } from "../../../data/area_registry"; import { forwardHaptic } from "../../../data/haptics"; From 2241807745df0d86799191c9791a24c508d8b6cb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 2 Jul 2025 11:03:21 +0200 Subject: [PATCH 184/216] Fix UI jump when using drag and drop in areas strategy editor (#26026) --- src/components/ha-areas-floors-display-editor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-areas-floors-display-editor.ts b/src/components/ha-areas-floors-display-editor.ts index 5b372f932f..539df7ab09 100644 --- a/src/components/ha-areas-floors-display-editor.ts +++ b/src/components/ha-areas-floors-display-editor.ts @@ -78,6 +78,7 @@ export class HaAreasFloorsDisplayEditor extends LitElement { handle-selector=".handle" @item-moved=${this._floorMoved} .disabled=${this.disabled || !canReorderFloors} + invert-swap >
    ${repeat( From 8853bf6ea2633264db68fc918754eb8accf66efe Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 2 Jul 2025 13:41:26 +0200 Subject: [PATCH 185/216] Improve styling of the code editor in fullscreen mode (#26029) --- src/components/ha-code-editor.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts index 5cb78c7193..69387c89a3 100644 --- a/src/components/ha-code-editor.ts +++ b/src/components/ha-code-editor.ts @@ -584,11 +584,14 @@ export class HaCodeEditor extends ReactiveElement { :host(.fullscreen) { position: fixed !important; - top: var(--header-height, 56px) !important; - left: 0 !important; - right: 0 !important; - bottom: 0 !important; + top: calc(var(--header-height, 56px) + 8px) !important; + left: 8px !important; + right: 8px !important; + bottom: 8px !important; z-index: 9999 !important; + border-radius: 12px !important; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3) !important; + overflow: hidden !important; background-color: var( --code-editor-background-color, var(--card-background-color) From ce929aea46bf8566931bc729d8903355c7f3e59c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 2 Jul 2025 13:41:52 +0200 Subject: [PATCH 186/216] Disable fullscreen in trigger detail dialog (#26030) --- .../config/automation/trigger/ha-automation-trigger-row.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts index f9f4c28ec2..5cabb41266 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -566,6 +566,7 @@ export default class HaAutomationTriggerRow extends LitElement { text: html` From 6be25270fdc2d3eabfea05aa6e1ac099eb74793c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 2 Jul 2025 13:42:21 +0200 Subject: [PATCH 187/216] Dont fetch device actions on first updated (#26028) --- .../config/devices/ha-config-device-page.ts | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 34f511b9c0..c062dc699f 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -10,7 +10,7 @@ import { mdiPlusCircle, mdiRestore, } from "@mdi/js"; -import type { CSSResultGroup, TemplateResult } from "lit"; +import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; @@ -273,22 +273,24 @@ export class HaConfigDevicePage extends LitElement { findBatteryChargingEntity(this.hass, entities) ); - public willUpdate(changedProps) { + public willUpdate(changedProps: PropertyValues) { super.willUpdate(changedProps); - if (changedProps.has("deviceId") || changedProps.has("entries")) { + if (changedProps.has("deviceId")) { this._deviceActions = []; this._deviceAlerts = []; this._deleteButtons = []; this._diagnosticDownloadLinks = []; + } + + if (changedProps.has("deviceId") || changedProps.has("entries")) { this._fetchData(); } } - protected firstUpdated(changedProps) { + protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); loadDeviceRegistryDetailDialog(); - this._fetchData(); } protected updated(changedProps) { @@ -989,6 +991,7 @@ export class HaConfigDevicePage extends LitElement { } private _getDeleteActions() { + const deviceId = this.deviceId; const device = this.hass.devices[this.deviceId]; if (!device) { @@ -1058,12 +1061,18 @@ export class HaConfigDevicePage extends LitElement { } ); + if (this.deviceId !== deviceId) { + // abort if the device has changed + return; + } + if (buttons.length > 0) { this._deleteButtons = buttons; } } private async _getDeviceActions() { + const deviceId = this.deviceId; const device = this.hass.devices[this.deviceId]; if (!device) { @@ -1157,14 +1166,25 @@ export class HaConfigDevicePage extends LitElement { // load matter device actions async to avoid an UI with 0 actions when the matter integration needs very long to get node diagnostics matter.getMatterDeviceActions(this, this.hass, device).then((actions) => { + if (this.deviceId !== deviceId) { + // abort if the device has changed + return; + } this._deviceActions = [...actions, ...(this._deviceActions || [])]; }); } + if (this.deviceId !== deviceId) { + // abort if the device has changed + return; + } + this._deviceActions = deviceActions; } private async _getDeviceAlerts() { + const deviceId = this.deviceId; + const device = this.hass.devices[this.deviceId]; if (!device) { @@ -1188,6 +1208,11 @@ export class HaConfigDevicePage extends LitElement { deviceAlerts.push(...alerts); } + if (this.deviceId !== deviceId) { + // abort if the device has changed + return; + } + this._deviceAlerts = deviceAlerts; if (deviceAlerts.length) { this._deviceAlertsActionsTimeout = window.setTimeout(() => { From 3e1341a7314ef4a68a1a7c5b611582a144a0b380 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 2 Jul 2025 04:43:06 -0700 Subject: [PATCH 188/216] Fix glitchy 'show' checkboxes on integration page (#26021) --- .../config/integrations/ha-config-integrations-dashboard.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integrations-dashboard.ts b/src/panels/config/integrations/ha-config-integrations-dashboard.ts index 3cb99481cd..f3a8e1eb4a 100644 --- a/src/panels/config/integrations/ha-config-integrations-dashboard.ts +++ b/src/panels/config/integrations/ha-config-integrations-dashboard.ts @@ -406,11 +406,7 @@ class HaConfigIntegrationsDashboard extends KeyboardShortcutMixin( ${!this._showDisabled && this.narrow && disabledConfigEntries.length ? html`${disabledConfigEntries.length}` : ""} - + Date: Wed, 2 Jul 2025 17:18:39 +0200 Subject: [PATCH 189/216] Fix zoom in statistic chart (#26034) --- src/components/chart/ha-chart-base.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index fdaad85c83..7a7fa68567 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -390,6 +390,7 @@ export class HaChartBase extends LitElement { type: "inside", orient: "horizontal", filterMode: "none", + xAxisIndex: 0, moveOnMouseMove: !this._isTouchDevice || this._isZoomed, preventDefaultMouseMove: !this._isTouchDevice || this._isZoomed, zoomLock: !this._isTouchDevice && !this._modifierPressed, From 252fd2bb6c0c313da6e0eb05bfbec2e38c6e6026 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:19:41 +0200 Subject: [PATCH 190/216] Update dependency @bundle-stats/plugin-webpack-filter to v4.21.0 (#26032) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 55c48c413e..d6f35fe472 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "@babel/helper-define-polyfill-provider": "0.6.5", "@babel/plugin-transform-runtime": "7.27.4", "@babel/preset-env": "7.27.2", - "@bundle-stats/plugin-webpack-filter": "4.20.2", + "@bundle-stats/plugin-webpack-filter": "4.21.0", "@lokalise/node-api": "14.8.0", "@octokit/auth-oauth-device": "8.0.1", "@octokit/plugin-retry": "8.0.1", diff --git a/yarn.lock b/yarn.lock index 37d4fae219..4759665a81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1165,14 +1165,14 @@ __metadata: languageName: node linkType: hard -"@bundle-stats/plugin-webpack-filter@npm:4.20.2": - version: 4.20.2 - resolution: "@bundle-stats/plugin-webpack-filter@npm:4.20.2" +"@bundle-stats/plugin-webpack-filter@npm:4.21.0": + version: 4.21.0 + resolution: "@bundle-stats/plugin-webpack-filter@npm:4.21.0" dependencies: tslib: "npm:2.8.1" peerDependencies: core-js: ^3.0.0 - checksum: 10/6b72c91fc55d3a312d9423ed0b682b02b209f612f9eeabcc379a561856572a91a373ebd27855eb3ab5793218add06fe453f20aa07d85315ecf722ce65f1870fa + checksum: 10/791cf529099e7a270227260305a711a59a85dc64846a9599f7705f36a6776531b97fc08e04c43b72ad3e0af008839fa8ea07bb4ab50bfc38b272ac5fa6249891 languageName: node linkType: hard @@ -9309,7 +9309,7 @@ __metadata: "@babel/preset-env": "npm:7.27.2" "@babel/runtime": "npm:7.27.6" "@braintree/sanitize-url": "npm:7.1.1" - "@bundle-stats/plugin-webpack-filter": "npm:4.20.2" + "@bundle-stats/plugin-webpack-filter": "npm:4.21.0" "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/commands": "npm:6.8.1" "@codemirror/language": "npm:6.11.2" From 2b88669a72768c3b572fefcef47082f1d2ad7b02 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:20:13 +0200 Subject: [PATCH 191/216] Update dependency eslint-plugin-lit-a11y to v5.1.0 (#26020) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index d6f35fe472..9d229a10db 100644 --- a/package.json +++ b/package.json @@ -190,7 +190,7 @@ "eslint-import-resolver-webpack": "0.13.10", "eslint-plugin-import": "2.32.0", "eslint-plugin-lit": "2.1.1", - "eslint-plugin-lit-a11y": "5.0.1", + "eslint-plugin-lit-a11y": "5.1.0", "eslint-plugin-unused-imports": "4.1.4", "eslint-plugin-wc": "3.0.1", "fancy-log": "2.0.0", diff --git a/yarn.lock b/yarn.lock index 4759665a81..36840fb0de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8036,9 +8036,9 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-lit-a11y@npm:5.0.1": - version: 5.0.1 - resolution: "eslint-plugin-lit-a11y@npm:5.0.1" +"eslint-plugin-lit-a11y@npm:5.1.0": + version: 5.1.0 + resolution: "eslint-plugin-lit-a11y@npm:5.1.0" dependencies: "@thepassle/axobject-query": "npm:^4.0.0" aria-query: "npm:^5.1.3" @@ -8052,7 +8052,7 @@ __metadata: parse5-htmlparser2-tree-adapter: "npm:^6.0.1" peerDependencies: eslint: ">= 5" - checksum: 10/1918deb146862d19b3d3a74d5a59e00184575b8083f8b3d44d91881cc24342f090c27039723f2bb85dc9cd990f7eaa3577098d329d8eed876a37bb6a0f2b63e2 + checksum: 10/b939fd3ea2d847c9c45eeed9b36c978d08a7270d32d519b99c8feef8f7e45d38e73399a2e69064647ba23348e2d7fa4ec9f05f168c1331c8b39c2ce9f74318cb languageName: node linkType: hard @@ -9426,7 +9426,7 @@ __metadata: eslint-import-resolver-webpack: "npm:0.13.10" eslint-plugin-import: "npm:2.32.0" eslint-plugin-lit: "npm:2.1.1" - eslint-plugin-lit-a11y: "npm:5.0.1" + eslint-plugin-lit-a11y: "npm:5.1.0" eslint-plugin-unused-imports: "npm:4.1.4" eslint-plugin-wc: "npm:3.0.1" fancy-log: "npm:2.0.0" From 978f9b0f838c9f89bf0f845a804753adaee0b311 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 2 Jul 2025 18:08:31 +0200 Subject: [PATCH 192/216] Reduce media selector size (#26033) --- .../ha-selector/ha-selector-media.ts | 167 ++++++++++-------- 1 file changed, 95 insertions(+), 72 deletions(-) diff --git a/src/components/ha-selector/ha-selector-media.ts b/src/components/ha-selector/ha-selector-media.ts index def2f3d9bc..c4b3a25f11 100644 --- a/src/components/ha-selector/ha-selector-media.ts +++ b/src/components/ha-selector/ha-selector-media.ts @@ -24,6 +24,10 @@ const MANUAL_SCHEMA = [ { name: "media_content_type", required: false, selector: { text: {} } }, ] as const; +const INCLUDE_DOMAINS = ["media_player"]; + +const EMPTY_FORM = {}; + @customElement("ha-selector-media") export class HaMediaSelector extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -84,7 +88,7 @@ export class HaMediaSelector extends LitElement { (stateObj && supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)); - const hasAccept = this.selector.media?.accept?.length; + const hasAccept = this.selector?.media?.accept?.length; return html` ${hasAccept @@ -100,7 +104,7 @@ export class HaMediaSelector extends LitElement { .disabled=${this.disabled} .helper=${this.helper} .required=${this.required} - include-domains='["media_player"]' + .includeDomains=${INCLUDE_DOMAINS} allow-custom-entity @value-changed=${this._entityChanged} > @@ -114,7 +118,7 @@ export class HaMediaSelector extends LitElement { @@ -122,63 +126,62 @@ export class HaMediaSelector extends LitElement { : html` -
    - ${this.value?.metadata?.thumbnail - ? html` -
    - ` - : html` -
    - -
    - `} -
    -
    - ${!this.value?.media_content_id - ? this.hass.localize( - "ui.components.selectors.media.pick_media" - ) - : this.value.metadata?.title || this.value.media_content_id} +
    +
    + ${this.value?.metadata?.thumbnail + ? html` +
    + ` + : html` +
    + +
    + `} +
    +
    + ${!this.value?.media_content_id + ? this.hass.localize( + "ui.components.selectors.media.pick_media" + ) + : this.value.metadata?.title || this.value.media_content_id} +
    `} @@ -229,6 +232,13 @@ export class HaMediaSelector extends LitElement { }); } + private _handleKeyDown(ev: KeyboardEvent) { + if (ev.key === "Enter" || ev.key === " ") { + ev.preventDefault(); + this._pickMedia(); + } + } + static styles = css` ha-entity-picker { display: block; @@ -243,41 +253,52 @@ export class HaMediaSelector extends LitElement { } ha-card { position: relative; - width: 200px; + width: 100%; box-sizing: border-box; cursor: pointer; + transition: background-color 180ms ease-in-out; + min-height: 56px; + } + ha-card:hover:not(.disabled), + ha-card:focus:not(.disabled) { + background-color: var(--state-icon-hover-color, rgba(0, 0, 0, 0.04)); + } + ha-card:focus { + outline: none; } ha-card.disabled { pointer-events: none; color: var(--disabled-text-color); } + .content-container { + display: flex; + align-items: center; + padding: 8px; + gap: 12px; + } ha-card .thumbnail { - width: 100%; + width: 40px; + height: 40px; + flex-shrink: 0; position: relative; box-sizing: border-box; - transition: padding-bottom 0.1s ease-out; - padding-bottom: 100%; - } - ha-card .thumbnail.portrait { - padding-bottom: 150%; + border-radius: 8px; + overflow: hidden; } ha-card .image { - border-radius: 3px 3px 0 0; + border-radius: 8px; } .folder { - --mdc-icon-size: calc(var(--media-browse-item-size, 175px) * 0.4); + --mdc-icon-size: 24px; } .title { - font-size: var(--ha-font-size-l); - padding-top: 16px; + font-size: var(--ha-font-size-m); overflow: hidden; text-overflow: ellipsis; - margin-bottom: 16px; - padding-left: 16px; - padding-right: 4px; - padding-inline-start: 16px; - padding-inline-end: 4px; white-space: nowrap; + line-height: 1.4; + flex: 1; + min-width: 0; } .image { position: absolute; @@ -290,13 +311,15 @@ export class HaMediaSelector extends LitElement { background-position: center; } .centered-image { - margin: 0 8px; + margin: 4px; background-size: contain; } .icon-holder { display: flex; justify-content: center; align-items: center; + width: 100%; + height: 100%; } `; } From 6e7874c2c99c3e24c1be603370316287afd92db1 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 2 Jul 2025 19:30:06 +0200 Subject: [PATCH 193/216] Fix play media action (#26035) --- .../types/ha-automation-action-play_media.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/panels/config/automation/action/types/ha-automation-action-play_media.ts b/src/panels/config/automation/action/types/ha-automation-action-play_media.ts index 9259d4ef62..0b77eba251 100644 --- a/src/panels/config/automation/action/types/ha-automation-action-play_media.ts +++ b/src/panels/config/automation/action/types/ha-automation-action-play_media.ts @@ -2,12 +2,19 @@ import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../../common/dom/fire_event"; -import "../../../../../components/ha-selector/ha-selector-media"; +import "../../../../../components/ha-selector/ha-selector"; import type { PlayMediaAction } from "../../../../../data/script"; -import type { MediaSelectorValue } from "../../../../../data/selector"; +import type { + MediaSelectorValue, + Selector, +} from "../../../../../data/selector"; import type { HomeAssistant } from "../../../../../types"; import type { ActionElement } from "../ha-automation-action-row"; +const MEDIA_SELECTOR_SCHEMA: Selector = { + media: {}, +}; + @customElement("ha-automation-action-play_media") export class HaPlayMediaAction extends LitElement implements ActionElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -38,12 +45,13 @@ export class HaPlayMediaAction extends LitElement implements ActionElement { protected render() { return html` - + > `; } From 002e9ad07123787f4d4f786aa48726ae61305f19 Mon Sep 17 00:00:00 2001 From: c0ffeeca7 <38767475+c0ffeeca7@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:51:35 +0200 Subject: [PATCH 194/216] Terminology: change controller to adapter (#26051) * Terminology: change controller to adapter * Update src/translations/en.json Co-authored-by: AlCalzone * Apply suggestions from code review --------- Co-authored-by: AlCalzone --- src/translations/en.json | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/translations/en.json b/src/translations/en.json index 948e4113cd..c861c420b0 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5890,7 +5890,7 @@ "not_ready": "{count} not ready", "nvm_backup": { "title": "Backup and restore", - "description": "Back up or restore your Z-Wave controller's non-volatile memory (NVM). The NVM contains your network information including paired devices. It's recommended to create a backup before making any major changes to your Z-Wave network.", + "description": "Back up or restore your Z-Wave adapter's non-volatile memory (NVM). The NVM contains your network information including paired devices. It's recommended to create a backup before making any major changes to your Z-Wave network.", "download_backup": "Download backup", "restore_backup": "Restore from backup", "backup_failed": "Failed to download backup", @@ -5898,21 +5898,21 @@ "restore_failed": "Failed to restore backup", "creating": "Creating backup", "restoring": "Restoring backup", - "migrate": "Migrate controller" + "migrate": "Migrate adapter" }, "statistics": { - "title": "Controller statistics", + "title": "Adapter statistics", "messages_tx": { "label": "Messages TX", - "tooltip": "Number of messages successfully sent to the controller" + "tooltip": "Number of messages successfully sent to the adapter" }, "messages_rx": { "label": "Messages RX", - "tooltip": "Number of messages successfully received by the controller" + "tooltip": "Number of messages successfully received by the adapter" }, "messages_dropped_tx": { "label": "Dropped messages TX", - "tooltip": "Number of messages from the controller that were dropped by the host" + "tooltip": "Number of messages from the adapter that were dropped by the host" }, "messages_dropped_rx": { "label": "Dropped messages RX", @@ -5920,23 +5920,23 @@ }, "nak": { "label": "NAK", - "tooltip": "Number of messages that the controller did not accept" + "tooltip": "Number of messages that the adapter did not accept" }, "can": { "label": "CAN", - "tooltip": "Number of collisions while sending a message to the controller" + "tooltip": "Number of collisions while sending a message to the adapter" }, "timeout_ack": { "label": "Timeout ACK", - "tooltip": "Number of transmission attempts where an ACK was missing from the controller" + "tooltip": "Number of transmission attempts where an ACK was missing from the adapter" }, "timeout_response": { "label": "Timeout response", - "tooltip": "Number of transmission attempts where the controller response did not come in time" + "tooltip": "Number of transmission attempts where the adapter response did not come in time" }, "timeout_callback": { "label": "Timeout callback", - "tooltip": "Number of transmission attempts where the controller callback did not come in time" + "tooltip": "Number of transmission attempts where the adapter callback did not come in time" } } }, @@ -5959,18 +5959,18 @@ }, "hard_reset_controller": { "NotStarted": { - "title": "Reset controller to factory settings", - "body": "If you decide to move forward, you will reset your controller to factory settings. As a result, the controller will forget all devices it is paired with and all Z-Wave devices for this network will be removed from Home Assistant. If there are any devices still paired with the controller when it is reset, they will have to go through the exclusion process before they can be re-paired. Would you like to continue?" + "title": "Reset adapter to factory settings", + "body": "If you decide to move forward, you will reset your adapter to factory settings. As a result, the adapter will forget all devices it is paired with and all Z-Wave devices for this network will be removed from Home Assistant. If there are any devices still paired with the adapter when it is reset, they will have to go through the exclusion process before they can be re-paired. Would you like to continue?" }, "InProgress": { - "title": "Resetting controller", - "body": "Your controller is being reset and restarted. Wait until the process is complete before closing this dialog" + "title": "Resetting adapter", + "body": "Your adapter is being reset and restarted. Wait until the process is complete before closing this dialog" }, "Done": { - "title": "Controller reset complete", - "body": "Your controller has been reset to factory settings and has been restarted! You can now close this dialog." + "title": "Adapter reset complete", + "body": "Your adapter has been reset to factory settings and has been restarted! You can now close this dialog." }, - "confirmation": "This action cannot be undone unless you have an NVM backup from your controller." + "confirmation": "This action cannot be undone unless you have a backup from your adapter." }, "node_statistics": { "title": "Device statistics", @@ -6035,7 +6035,7 @@ }, "rssi": { "label": "RSSI", - "tooltip": "The RSSI of the ACK frame received by the controller" + "tooltip": "The RSSI of the ACK frame received by the adapter" }, "route_failed_between": { "label": "Route failed between", @@ -6254,7 +6254,7 @@ }, "rebuild_node_routes": { "title": "Rebuild routes for a Z-Wave device", - "introduction": "Tell {device} to update its routes back to the controller. This can help with communication issues if you have recently moved the device or your controller.", + "introduction": "Assign new routes between {device} and the adapter. This can help with communication issues if you have recently moved the device or your adapter.", "traffic_warning": "The route rebuilding process generates a large amount of traffic on the Z-Wave network. This may cause devices to respond slowly (or not at all) while the rebuilding is in progress.", "start_rebuilding_routes": "Rebuild Routes for Device", "rebuilding_routes_failed": "{device} routes could not be rebuild.", @@ -6266,7 +6266,7 @@ "update_firmware": { "title": "Update device firmware", "warning": "WARNING: Firmware updates can brick your device if you do not correctly follow the manufacturer's guidance. The Home Assistant and Z-Wave JS teams do not take any responsibility for any damages to your device as a result of the firmware update and will not be able to help you if you brick your device. Would you still like to continue?", - "warning_controller": "WARNING: Firmware updates can brick your controller if you do not use the right firmware files, or if you attempt to stop the firmware update before it completes. The Home Assistant and Z-Wave JS teams do not take any responsibility for any damages to your controller as a result of the firmware update and will not be able to help you if you brick your controller. Would you still like to continue?", + "warning_controller": "WARNING: Firmware updates can brick your adapter if you do not use the right firmware files, or if you attempt to stop the firmware update before it completes. The Home Assistant and Z-Wave JS teams do not take any responsibility for any damages to your adapter as a result of the firmware update and will not be able to help you if you brick your adapter. Would you still like to continue?", "introduction": "Select the firmware file you would like to use to update {device}.", "introduction_controller": "Select the firmware file you would like to use to update {device}. Note that once you start a firmware update, you MUST wait for the update to complete.", "firmware_target_intro": "Select the firmware target (0 for the Z-Wave chip, ≥1 for other chips if they exist) for this update.", @@ -6287,7 +6287,7 @@ "error": "Unable to update firmware on {device}: {message}.", "try_again": "To attempt the firmware update again, select the new firmware file you would like to use.", "done": "The firmware update is complete! If you want to attempt another firmware update on this device, please wait until it gets re-interviewed.", - "done_controller": "The firmware update is complete! Your controller is being restarted and your network will temporarily be unavailable.", + "done_controller": "The firmware update is complete! Your adapter is being restarted and your network will temporarily be unavailable.", "Error_Timeout": "Timed out", "Error_Checksum": "Checksum error", "Error_TransmissionFailed": "Transmission failed", From dba6a3c756eb136a6afa3d15a9c6a1f1fdf7b6e4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 3 Jul 2025 17:14:49 +0300 Subject: [PATCH 195/216] Update fullcalendar monorepo to v6.1.18 (#26047) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 14 +++++----- yarn.lock | 72 ++++++++++++++++++++++++++-------------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 9d229a10db..172251dc24 100644 --- a/package.json +++ b/package.json @@ -45,12 +45,12 @@ "@formatjs/intl-numberformat": "8.15.4", "@formatjs/intl-pluralrules": "5.4.4", "@formatjs/intl-relativetimeformat": "11.4.11", - "@fullcalendar/core": "6.1.17", - "@fullcalendar/daygrid": "6.1.17", - "@fullcalendar/interaction": "6.1.17", - "@fullcalendar/list": "6.1.17", - "@fullcalendar/luxon3": "6.1.17", - "@fullcalendar/timegrid": "6.1.17", + "@fullcalendar/core": "6.1.18", + "@fullcalendar/daygrid": "6.1.18", + "@fullcalendar/interaction": "6.1.18", + "@fullcalendar/list": "6.1.18", + "@fullcalendar/luxon3": "6.1.18", + "@fullcalendar/timegrid": "6.1.18", "@lezer/highlight": "1.2.1", "@lit-labs/motion": "1.0.8", "@lit-labs/observers": "2.0.5", @@ -231,7 +231,7 @@ "lit-html": "3.3.0", "clean-css": "5.3.3", "@lit/reactive-element": "2.1.0", - "@fullcalendar/daygrid": "6.1.17", + "@fullcalendar/daygrid": "6.1.18", "globals": "16.2.0", "tslib": "2.8.1", "@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch" diff --git a/yarn.lock b/yarn.lock index 36840fb0de..80b86adf8b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1810,60 +1810,60 @@ __metadata: languageName: node linkType: hard -"@fullcalendar/core@npm:6.1.17": - version: 6.1.17 - resolution: "@fullcalendar/core@npm:6.1.17" +"@fullcalendar/core@npm:6.1.18": + version: 6.1.18 + resolution: "@fullcalendar/core@npm:6.1.18" dependencies: preact: "npm:~10.12.1" - checksum: 10/797a097b8a27afdafa52b0612762490ef7e46d350c9d2eb3364fa7ed44c3384810c18aff46c6fe56620af84b3fb33e42679c24570cdc457cb4460cd9119b5838 + checksum: 10/0e29608599f1b4f42c0e6381a29739a1aafb226e37f085ba0405ee806c2c1417fa3dc013b375749e1a284eb94e4949b9573990816145874cc10aaaaacc7d8756 languageName: node linkType: hard -"@fullcalendar/daygrid@npm:6.1.17": - version: 6.1.17 - resolution: "@fullcalendar/daygrid@npm:6.1.17" +"@fullcalendar/daygrid@npm:6.1.18": + version: 6.1.18 + resolution: "@fullcalendar/daygrid@npm:6.1.18" peerDependencies: - "@fullcalendar/core": ~6.1.17 - checksum: 10/4bb443a7d85f2714952c7d8f5daaa9fb28847cd4f00b5fcfa0a308d5e52a185a76553c1755f6082297495365584b2a99ae687554d4060e3bd15de9371dd27776 + "@fullcalendar/core": ~6.1.18 + checksum: 10/a91dc05445b7ad9210fb964d0e3bd378c74660a9f4d32ca3b28d84a79060795335ca8fd3b01277513e912eaeac37156d50976df281abf78a8ec5c79b5c93952e languageName: node linkType: hard -"@fullcalendar/interaction@npm:6.1.17": - version: 6.1.17 - resolution: "@fullcalendar/interaction@npm:6.1.17" +"@fullcalendar/interaction@npm:6.1.18": + version: 6.1.18 + resolution: "@fullcalendar/interaction@npm:6.1.18" peerDependencies: - "@fullcalendar/core": ~6.1.17 - checksum: 10/b26ff0085a25c6b98d3cbaad99d91f76128c7a8045085e80a2b9383464bbc4b561ee42e068aaf7e094f6a0161507ebc2efc6bc28e94c67c771aa7506055e480e + "@fullcalendar/core": ~6.1.18 + checksum: 10/6958abef8a3a677c10fb8900f744019c35a7afe62dd0f031f9cb91812303cd2b3bbca6eb1443573a4d9e37a25f64472668e38247ffbed980416245867c8689f8 languageName: node linkType: hard -"@fullcalendar/list@npm:6.1.17": - version: 6.1.17 - resolution: "@fullcalendar/list@npm:6.1.17" +"@fullcalendar/list@npm:6.1.18": + version: 6.1.18 + resolution: "@fullcalendar/list@npm:6.1.18" peerDependencies: - "@fullcalendar/core": ~6.1.17 - checksum: 10/ddfda1ae722c3e21794247739727faaf5dc4d0884e805ba01557a4b7a21f612a886a03a4028ffbad76cc940879ddcf6254e7c8a23f2404516638af2d4ed55d30 + "@fullcalendar/core": ~6.1.18 + checksum: 10/5d352a3b2311d9dda84a72ef5f3c990ede7f57f64ae9374629db8139bac26584748600b7af7686d39e7e88d10c16bfc60208fa9dd5727c8e343c6652e81a9ac0 languageName: node linkType: hard -"@fullcalendar/luxon3@npm:6.1.17": - version: 6.1.17 - resolution: "@fullcalendar/luxon3@npm:6.1.17" +"@fullcalendar/luxon3@npm:6.1.18": + version: 6.1.18 + resolution: "@fullcalendar/luxon3@npm:6.1.18" peerDependencies: - "@fullcalendar/core": ~6.1.17 + "@fullcalendar/core": ~6.1.18 luxon: ^3.0.0 - checksum: 10/3c3341ec698c52686567b66f5be950070241bccec0536051ade25989453372344eaf488caf2fa52a0aeca1466246cd9e1d5be05a241273ba798b882e78284237 + checksum: 10/e7426e5dd4386977adf1469e5f52178e5e6b87d7c1931e8b928699db7c9a759c4e564971614f01d8463896e2c31767f2f104d7b3a0a74c08ca568396afeac2bd languageName: node linkType: hard -"@fullcalendar/timegrid@npm:6.1.17": - version: 6.1.17 - resolution: "@fullcalendar/timegrid@npm:6.1.17" +"@fullcalendar/timegrid@npm:6.1.18": + version: 6.1.18 + resolution: "@fullcalendar/timegrid@npm:6.1.18" dependencies: - "@fullcalendar/daygrid": "npm:~6.1.17" + "@fullcalendar/daygrid": "npm:~6.1.18" peerDependencies: - "@fullcalendar/core": ~6.1.17 - checksum: 10/b19fd9d6d9af301a0834dea29f7bf90d1b457091cc62cd09c0247d98f0858f050c773ef14e3c79af84aafffe4ee9c5aa78f8b2c7db8b372db4e3e1ed5e03ad05 + "@fullcalendar/core": ~6.1.18 + checksum: 10/6eff8fff54fd5452fc47430bd52c28e70ee5eb58780de4a98671e290911ad0c265fa763452272c65e2bd304d7ac4aecb92d3c526b265f222ebb60274a366199f languageName: node linkType: hard @@ -9327,12 +9327,12 @@ __metadata: "@formatjs/intl-numberformat": "npm:8.15.4" "@formatjs/intl-pluralrules": "npm:5.4.4" "@formatjs/intl-relativetimeformat": "npm:11.4.11" - "@fullcalendar/core": "npm:6.1.17" - "@fullcalendar/daygrid": "npm:6.1.17" - "@fullcalendar/interaction": "npm:6.1.17" - "@fullcalendar/list": "npm:6.1.17" - "@fullcalendar/luxon3": "npm:6.1.17" - "@fullcalendar/timegrid": "npm:6.1.17" + "@fullcalendar/core": "npm:6.1.18" + "@fullcalendar/daygrid": "npm:6.1.18" + "@fullcalendar/interaction": "npm:6.1.18" + "@fullcalendar/list": "npm:6.1.18" + "@fullcalendar/luxon3": "npm:6.1.18" + "@fullcalendar/timegrid": "npm:6.1.18" "@lezer/highlight": "npm:1.2.1" "@lit-labs/motion": "npm:1.0.8" "@lit-labs/observers": "npm:2.0.5" From d605b67b41c93af7e31a430733841c8612d7b89c Mon Sep 17 00:00:00 2001 From: Ezra Freedman <38084742+ezra-freedman@users.noreply.github.com> Date: Thu, 3 Jul 2025 15:19:48 -0400 Subject: [PATCH 196/216] Prevent uncaught TypeError on HuiWeatherForecastCard render (#26038) --- .../lovelace/cards/hui-weather-forecast-card.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts index 2140038978..7c2759102d 100644 --- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts +++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts @@ -243,11 +243,11 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { ); let itemsToShow = this._config?.forecast_slots ?? 5; - if (this._sizeController.value.width === "very-very-narrow") { + if (this._sizeController.value?.width === "very-very-narrow") { itemsToShow = Math.min(3, itemsToShow); - } else if (this._sizeController.value.width === "very-narrow") { + } else if (this._sizeController.value?.width === "very-narrow") { itemsToShow = Math.min(5, itemsToShow); - } else if (this._sizeController.value.width === "narrow") { + } else if (this._sizeController.value?.width === "narrow") { itemsToShow = Math.min(7, itemsToShow); } @@ -266,8 +266,12 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { return html` Date: Fri, 4 Jul 2025 10:16:33 +0300 Subject: [PATCH 197/216] Update dependency typescript-eslint to v8.35.1 (#26058) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 142 +++++++++++++++++++++++++-------------------------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index 172251dc24..0100aa7d9a 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,7 @@ "terser-webpack-plugin": "5.3.14", "ts-lit-plugin": "2.0.2", "typescript": "5.8.3", - "typescript-eslint": "8.35.0", + "typescript-eslint": "8.35.1", "vite-tsconfig-paths": "5.1.4", "vitest": "3.2.4", "webpack-stats-plugin": "1.1.3", diff --git a/yarn.lock b/yarn.lock index 80b86adf8b..9b8fe7f085 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4940,105 +4940,105 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.35.0" +"@typescript-eslint/eslint-plugin@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/eslint-plugin@npm:8.35.1" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.35.0" - "@typescript-eslint/type-utils": "npm:8.35.0" - "@typescript-eslint/utils": "npm:8.35.0" - "@typescript-eslint/visitor-keys": "npm:8.35.0" + "@typescript-eslint/scope-manager": "npm:8.35.1" + "@typescript-eslint/type-utils": "npm:8.35.1" + "@typescript-eslint/utils": "npm:8.35.1" + "@typescript-eslint/visitor-keys": "npm:8.35.1" graphemer: "npm:^1.4.0" ignore: "npm:^7.0.0" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.1.0" peerDependencies: - "@typescript-eslint/parser": ^8.35.0 + "@typescript-eslint/parser": ^8.35.1 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/a87ef0ed958bfb1d2fd38f41d1628d5411136c201e1361ba2ea136a3e9ec01516abf3d4e963eee1bba60132a630814a8af06f4cc014fafd578d8b45f62771eb0 + checksum: 10/22c4ff7503e4919449b996453ff29ba46e5c0024fac883ac41a313482454f13d55937789f499395dc2a7dba25b1ad47ac5295d60b118f2fa54ca768228514662 languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/parser@npm:8.35.0" +"@typescript-eslint/parser@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/parser@npm:8.35.1" dependencies: - "@typescript-eslint/scope-manager": "npm:8.35.0" - "@typescript-eslint/types": "npm:8.35.0" - "@typescript-eslint/typescript-estree": "npm:8.35.0" - "@typescript-eslint/visitor-keys": "npm:8.35.0" + "@typescript-eslint/scope-manager": "npm:8.35.1" + "@typescript-eslint/types": "npm:8.35.1" + "@typescript-eslint/typescript-estree": "npm:8.35.1" + "@typescript-eslint/visitor-keys": "npm:8.35.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/bd0e406938a4b305bb409611c29bcdc01e3a63c1c47fcbf527a7e2033ae71f6de7c7f95aa4f8973dcd38f5ec26f8e2857e01f160ebc538453161735c17b644da + checksum: 10/d5e0ecdb945c90fc1fea3f7dd375e424f1a6d49a97627ad24330499d573d45f85348e05a97e3a4643aec5ad9d210073487687872bd573abd79923a12fc46e716 languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/project-service@npm:8.35.0" +"@typescript-eslint/project-service@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/project-service@npm:8.35.1" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.35.0" - "@typescript-eslint/types": "npm:^8.35.0" + "@typescript-eslint/tsconfig-utils": "npm:^8.35.1" + "@typescript-eslint/types": "npm:^8.35.1" debug: "npm:^4.3.4" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/a9419da92231aa27f75078fcffab1d02398b50fdb7d5399775a414ba02570682b4b60cdfafb544a021b0dc2372f029c4195f5ae17c50deb11c25661b2ac18a74 + checksum: 10/f8ceb1c6ab7cdf2c7bc334e74d0d1cd86b5e563319c5477987a05f47af433543b281912ae0cdd875561dc2cc4d3ba4ed3bdd8d5bb6dba68bcde4f68a7d0967e7 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/scope-manager@npm:8.35.0" +"@typescript-eslint/scope-manager@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/scope-manager@npm:8.35.1" dependencies: - "@typescript-eslint/types": "npm:8.35.0" - "@typescript-eslint/visitor-keys": "npm:8.35.0" - checksum: 10/36082fe476cf744c016a554e5ce77e6beb7d4d9992b513382bdf7e8f7d044ffd780fefc3f698e53780ead677d0afaf93e82bade10f08933e2757750bfd273d13 + "@typescript-eslint/types": "npm:8.35.1" + "@typescript-eslint/visitor-keys": "npm:8.35.1" + checksum: 10/9124302c969126a50c70f9ccbefec0e5a771563b5518318d56fc6242c5cff61da74e7885832370ccd406a048edc300476b1723ad1845d41bd205879d95fbc6b6 languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.35.0, @typescript-eslint/tsconfig-utils@npm:^8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.35.0" +"@typescript-eslint/tsconfig-utils@npm:8.35.1, @typescript-eslint/tsconfig-utils@npm:^8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.35.1" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/4160928313ccbe8b169a009b9c1220826c7df7aab427f960c31f3b838931bc7a121ebee8040118481e4528e2e3cf1b26da047c6ac1d802ecff2ef7206026ea6b + checksum: 10/6b6176ec7dbfbe53539bce3e7554f062ff4d220aa5cb5793d52067fe6c196d749e77557dca66f5bf1ee23972e948d5c59461fa3e11da9e34a0a27d9fb7585f5a languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/type-utils@npm:8.35.0" +"@typescript-eslint/type-utils@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/type-utils@npm:8.35.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.35.0" - "@typescript-eslint/utils": "npm:8.35.0" + "@typescript-eslint/typescript-estree": "npm:8.35.1" + "@typescript-eslint/utils": "npm:8.35.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.1.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/796210b0f099a9382ab8a437556e2710fa481f5288e098d7c45e3c26827df762968241d8ac9542d805274e3755785b3afd1ad2018c79efce72a33515f2e5613d + checksum: 10/728df75bac6960192c18436a8340ed7a0f78b472486279f673e4018d493569f2278b7fcac78c5e0f7ccdb873ead227de6d94bc7aebf5cf046c4d8e53c5569bfd languageName: node linkType: hard -"@typescript-eslint/types@npm:8.35.0, @typescript-eslint/types@npm:^8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/types@npm:8.35.0" - checksum: 10/34b5e6da2c59ea84cd528608fff0cc14b102fd23f5517dfee4ef38c9372861d80b5bf92445c9679674f0a4f8dc4ded5066c1bca2bc5569c47515f94568984f35 +"@typescript-eslint/types@npm:8.35.1, @typescript-eslint/types@npm:^8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/types@npm:8.35.1" + checksum: 10/2d5b8f40b2ef0b7d439ee119d2ed12372097c4372aea7ff6d46f92fa743fc60619f8619192fbc0df3833d941be5d9bcb5129b8f6d029716ca86ba42514fbeff9 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.35.0" +"@typescript-eslint/typescript-estree@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/typescript-estree@npm:8.35.1" dependencies: - "@typescript-eslint/project-service": "npm:8.35.0" - "@typescript-eslint/tsconfig-utils": "npm:8.35.0" - "@typescript-eslint/types": "npm:8.35.0" - "@typescript-eslint/visitor-keys": "npm:8.35.0" + "@typescript-eslint/project-service": "npm:8.35.1" + "@typescript-eslint/tsconfig-utils": "npm:8.35.1" + "@typescript-eslint/types": "npm:8.35.1" + "@typescript-eslint/visitor-keys": "npm:8.35.1" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -5047,32 +5047,32 @@ __metadata: ts-api-utils: "npm:^2.1.0" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10/4dff7c5a8853c8f4e30d35565c62d3ad5bf8445309bd465d94e9bca725853012bb9f58896a04207c30e10b6669511caac8c0f080ed781c93a3db81d5808195aa + checksum: 10/b38a891a37e1c8d76bdb3e8039482b723df590bf9d192a5480ec6777a316914542f610a1d9070bc53e0642c511ddc4ee1c3c03ac0e04a5510feefa95307f51b7 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/utils@npm:8.35.0" +"@typescript-eslint/utils@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/utils@npm:8.35.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.35.0" - "@typescript-eslint/types": "npm:8.35.0" - "@typescript-eslint/typescript-estree": "npm:8.35.0" + "@typescript-eslint/scope-manager": "npm:8.35.1" + "@typescript-eslint/types": "npm:8.35.1" + "@typescript-eslint/typescript-estree": "npm:8.35.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/24b4af650a8f4d21515498c1c38624717f210d68aedc6cee6958f4e8c36504d871176800020764500f64e078dda1ce23c19bbe19f8f5f7efbe995eb1afca42f2 + checksum: 10/68388898dc095d7813a18049e782d90ed6500496bb68e3ea5efd7e1de24f37732b133bf88faca835b6219383f406693fdf846e16d3c48e9418388121c89dcf48 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.35.0": - version: 8.35.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.35.0" +"@typescript-eslint/visitor-keys@npm:8.35.1": + version: 8.35.1 + resolution: "@typescript-eslint/visitor-keys@npm:8.35.1" dependencies: - "@typescript-eslint/types": "npm:8.35.0" + "@typescript-eslint/types": "npm:8.35.1" eslint-visitor-keys: "npm:^4.2.1" - checksum: 10/c0acb13aac3a2be5e82844f7d2e86137347efdd04661dbf9fa69ef04a19dd2f1eb2f1eb6bfbfbaada78a46884308d2c0e0b5d0d1a094c84f2dfb670b67ac2b3b + checksum: 10/0add7a9c00e7b336797bb7378bd02b3ef31368a8e928afb2dbeec0cc4ab9f6413519e477f5c504d62b38d1dae3791f7ffda36d41b403411608628bff8dd123bd languageName: node linkType: hard @@ -9481,7 +9481,7 @@ __metadata: tinykeys: "npm:3.0.0" ts-lit-plugin: "npm:2.0.2" typescript: "npm:5.8.3" - typescript-eslint: "npm:8.35.0" + typescript-eslint: "npm:8.35.1" ua-parser-js: "npm:2.0.4" vis-data: "npm:7.1.9" vite-tsconfig-paths: "npm:5.1.4" @@ -14513,17 +14513,17 @@ __metadata: languageName: node linkType: hard -"typescript-eslint@npm:8.35.0": - version: 8.35.0 - resolution: "typescript-eslint@npm:8.35.0" +"typescript-eslint@npm:8.35.1": + version: 8.35.1 + resolution: "typescript-eslint@npm:8.35.1" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.35.0" - "@typescript-eslint/parser": "npm:8.35.0" - "@typescript-eslint/utils": "npm:8.35.0" + "@typescript-eslint/eslint-plugin": "npm:8.35.1" + "@typescript-eslint/parser": "npm:8.35.1" + "@typescript-eslint/utils": "npm:8.35.1" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10/350f584dfe2de1863954d65e27f8a02849569970ee4e0c108d881180d044ffeb4cc112a447ea835f347357219219afefed35084bd0bfd27496eb882f221eb5de + checksum: 10/56080cf28068e074cf6fa9f0a4002b54fe2c9ba319a7b0eccc5d0a4a76fecb8023fe83f209b983da2f0c782fbbd1c6a5fd680f9dd71e4a1f0e964fb6df4dd89e languageName: node linkType: hard From 382a47a082031b661f5a3e9e5b1731e5e9357539 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 15:11:40 +0200 Subject: [PATCH 198/216] Update rspack monorepo to v1.4.2 (#26066) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 110 +++++++++++++++++++++++++-------------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 0100aa7d9a..5283ed3359 100644 --- a/package.json +++ b/package.json @@ -159,8 +159,8 @@ "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", "@rsdoctor/rspack-plugin": "1.1.5", - "@rspack/cli": "1.4.1", - "@rspack/core": "1.4.1", + "@rspack/cli": "1.4.2", + "@rspack/core": "1.4.2", "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.22", "@types/chromecast-caf-sender": "1.0.11", diff --git a/yarn.lock b/yarn.lock index 9b8fe7f085..898d8e8596 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3935,92 +3935,92 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-darwin-arm64@npm:1.4.1" +"@rspack/binding-darwin-arm64@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-darwin-arm64@npm:1.4.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-darwin-x64@npm:1.4.1" +"@rspack/binding-darwin-x64@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-darwin-x64@npm:1.4.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.4.1" +"@rspack/binding-linux-arm64-gnu@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.4.2" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.4.1" +"@rspack/binding-linux-arm64-musl@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.4.2" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.4.1" +"@rspack/binding-linux-x64-gnu@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.4.2" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-linux-x64-musl@npm:1.4.1" +"@rspack/binding-linux-x64-musl@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-linux-x64-musl@npm:1.4.2" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-wasm32-wasi@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-wasm32-wasi@npm:1.4.1" +"@rspack/binding-wasm32-wasi@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-wasm32-wasi@npm:1.4.2" dependencies: "@napi-rs/wasm-runtime": "npm:^0.2.11" conditions: cpu=wasm32 languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.4.1" +"@rspack/binding-win32-arm64-msvc@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.4.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.4.1" +"@rspack/binding-win32-ia32-msvc@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.4.2" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.4.1" +"@rspack/binding-win32-x64-msvc@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.4.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/binding@npm:1.4.1" +"@rspack/binding@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/binding@npm:1.4.2" dependencies: - "@rspack/binding-darwin-arm64": "npm:1.4.1" - "@rspack/binding-darwin-x64": "npm:1.4.1" - "@rspack/binding-linux-arm64-gnu": "npm:1.4.1" - "@rspack/binding-linux-arm64-musl": "npm:1.4.1" - "@rspack/binding-linux-x64-gnu": "npm:1.4.1" - "@rspack/binding-linux-x64-musl": "npm:1.4.1" - "@rspack/binding-wasm32-wasi": "npm:1.4.1" - "@rspack/binding-win32-arm64-msvc": "npm:1.4.1" - "@rspack/binding-win32-ia32-msvc": "npm:1.4.1" - "@rspack/binding-win32-x64-msvc": "npm:1.4.1" + "@rspack/binding-darwin-arm64": "npm:1.4.2" + "@rspack/binding-darwin-x64": "npm:1.4.2" + "@rspack/binding-linux-arm64-gnu": "npm:1.4.2" + "@rspack/binding-linux-arm64-musl": "npm:1.4.2" + "@rspack/binding-linux-x64-gnu": "npm:1.4.2" + "@rspack/binding-linux-x64-musl": "npm:1.4.2" + "@rspack/binding-wasm32-wasi": "npm:1.4.2" + "@rspack/binding-win32-arm64-msvc": "npm:1.4.2" + "@rspack/binding-win32-ia32-msvc": "npm:1.4.2" + "@rspack/binding-win32-x64-msvc": "npm:1.4.2" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -4042,13 +4042,13 @@ __metadata: optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/565de02d4b5aabde5e53f56bb6515237d561308df45dd2c4406fd684a58c6a5b7928516af05e990c7e94ce67b48198285980f07393f79500e6ce8b283a549e00 + checksum: 10/b5bd023fadc6a7bf03c7c34e318842fcc0101be9fed3d94b031d957995ec71af33239b7709ee4f1e7f7725eb13adb6b4e15d66166245173a8c7db5080db4f72f languageName: node linkType: hard -"@rspack/cli@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/cli@npm:1.4.1" +"@rspack/cli@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/cli@npm:1.4.2" dependencies: "@discoveryjs/json-ext": "npm:^0.5.7" "@rspack/dev-server": "npm:~1.1.3" @@ -4062,23 +4062,23 @@ __metadata: "@rspack/core": ^1.0.0-alpha || ^1.x bin: rspack: bin/rspack.js - checksum: 10/ab62d3f1c7a157c22968a3b620f7c52011ea96c6145895db529104679bb7032ab769841db3e4667f1d6f13b406faad9d07092f44ac607b8c9dfcbe6337a8859f + checksum: 10/9ffaaf3cf2a07cbd67511291bb5f64e9aca838a974008d5d5c1c3ac78ca58f4ae72ca82a96f0b432628754913aed56b2f6c3a2f5b450f051522f33f6422e4abd languageName: node linkType: hard -"@rspack/core@npm:1.4.1": - version: 1.4.1 - resolution: "@rspack/core@npm:1.4.1" +"@rspack/core@npm:1.4.2": + version: 1.4.2 + resolution: "@rspack/core@npm:1.4.2" dependencies: "@module-federation/runtime-tools": "npm:0.15.0" - "@rspack/binding": "npm:1.4.1" + "@rspack/binding": "npm:1.4.2" "@rspack/lite-tapable": "npm:1.0.1" peerDependencies: "@swc/helpers": ">=0.5.1" peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/36d79846a7c7b26f6994feaa51e16ff5054e644011a70a0368315d472760ab4d3436fc900a6dee832085b5d4bb2b0d991919170e09fde599fe7525bf8dec8f9f + checksum: 10/c47addb078c200bf32fb76fe07dd90c3ad9aab245291b6a5d695868db238b9a3ef36526028ac67081e1ae46547afff6b94ddbb7bd5899948f008c94bc938df45 languageName: node linkType: hard @@ -9371,8 +9371,8 @@ __metadata: "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" "@rsdoctor/rspack-plugin": "npm:1.1.5" - "@rspack/cli": "npm:1.4.1" - "@rspack/core": "npm:1.4.1" + "@rspack/cli": "npm:1.4.2" + "@rspack/core": "npm:1.4.2" "@shoelace-style/shoelace": "npm:2.20.1" "@swc/helpers": "npm:0.5.17" "@thomasloven/round-slider": "npm:0.6.0" From 6619f064eb41c81d1618d9df9bc95644d41db364 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 15:12:03 +0200 Subject: [PATCH 199/216] Update dependency @lokalise/node-api to v14.9.0 (#26067) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5283ed3359..e9340f9d8c 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@babel/plugin-transform-runtime": "7.27.4", "@babel/preset-env": "7.27.2", "@bundle-stats/plugin-webpack-filter": "4.21.0", - "@lokalise/node-api": "14.8.0", + "@lokalise/node-api": "14.9.0", "@octokit/auth-oauth-device": "8.0.1", "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", diff --git a/yarn.lock b/yarn.lock index 898d8e8596..18e3852076 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2276,10 +2276,10 @@ __metadata: languageName: node linkType: hard -"@lokalise/node-api@npm:14.8.0": - version: 14.8.0 - resolution: "@lokalise/node-api@npm:14.8.0" - checksum: 10/4607baa060bfa710d25dee57d986203607a08541b64d1b561ae479559780282028173efcc087d6b24f8eccc5565c99740253b9495bada7083a5f00b5c9081dd9 +"@lokalise/node-api@npm:14.9.0": + version: 14.9.0 + resolution: "@lokalise/node-api@npm:14.9.0" + checksum: 10/d8960d60d563719e960acf63d4179246ee581e35f25c56b82a3c877f9dbd3260c6c27926a55a128c4a8c8979851b3fa3b9041964298cb8a09b5d2645152105be languageName: node linkType: hard @@ -9339,7 +9339,7 @@ __metadata: "@lit-labs/virtualizer": "npm:2.1.0" "@lit/context": "npm:1.1.5" "@lit/reactive-element": "npm:2.1.0" - "@lokalise/node-api": "npm:14.8.0" + "@lokalise/node-api": "npm:14.9.0" "@material/chips": "npm:=14.0.0-canary.53b3cad2f.0" "@material/data-table": "npm:=14.0.0-canary.53b3cad2f.0" "@material/mwc-base": "npm:0.27.0" From 257769cdc7d121e972c6458cb715f36114845be2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 18:42:09 +0200 Subject: [PATCH 200/216] Update dependency globals to v16.3.0 (#26068) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e9340f9d8c..22174fcd05 100644 --- a/package.json +++ b/package.json @@ -232,7 +232,7 @@ "clean-css": "5.3.3", "@lit/reactive-element": "2.1.0", "@fullcalendar/daygrid": "6.1.18", - "globals": "16.2.0", + "globals": "16.3.0", "tslib": "2.8.1", "@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch" }, diff --git a/yarn.lock b/yarn.lock index 18e3852076..c3d6c97ada 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9038,10 +9038,10 @@ __metadata: languageName: node linkType: hard -"globals@npm:16.2.0": - version: 16.2.0 - resolution: "globals@npm:16.2.0" - checksum: 10/37fc33502973ebbee5a44b58939aa8574abc00ca1fc4c1d4ec0571a2c6620843ae647eff8bd082adf6bb5975ad221a887522b9a7961125764f0cb6dfab0b7483 +"globals@npm:16.3.0": + version: 16.3.0 + resolution: "globals@npm:16.3.0" + checksum: 10/accb0939d993a1c461df8d961ce9911a9a96120929e0a61057ae8e75b7df0a8bf8089da0f4e3a476db0211156416fbd26e222a56f74b389a140b34481c0a72b0 languageName: node linkType: hard From 4d932f0b4ac500a63475fa3096b665aaf882aafb Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 4 Jul 2025 11:06:49 -0700 Subject: [PATCH 201/216] Support translating number selector UoM (#26070) --- src/components/ha-selector/ha-selector-number.ts | 13 ++++++++++++- src/data/selector.ts | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/ha-selector/ha-selector-number.ts b/src/components/ha-selector/ha-selector-number.ts index 66fed152d9..f4aff5a7d5 100644 --- a/src/components/ha-selector/ha-selector-number.ts +++ b/src/components/ha-selector/ha-selector-number.ts @@ -23,6 +23,9 @@ export class HaNumberSelector extends LitElement { @property() public helper?: string; + @property({ attribute: false }) + public localizeValue?: (key: string) => string; + @property({ type: Boolean }) public required = true; @property({ type: Boolean }) public disabled = false; @@ -60,6 +63,14 @@ export class HaNumberSelector extends LitElement { } } + const translationKey = this.selector.number?.translation_key; + let unit = this.selector.number?.unit_of_measurement; + if (isBox && unit && this.localizeValue && translationKey) { + unit = + this.localizeValue(`${translationKey}.unit_of_measurement.${unit}`) || + unit; + } + return html` ${this.label && !isBox ? html`${this.label}${this.required ? "*" : ""}` @@ -97,7 +108,7 @@ export class HaNumberSelector extends LitElement { .helper=${isBox ? this.helper : undefined} .disabled=${this.disabled} .required=${this.required} - .suffix=${this.selector.number?.unit_of_measurement} + .suffix=${unit} type="number" autoValidate ?no-spinner=${!isBox} diff --git a/src/data/selector.ts b/src/data/selector.ts index abac290475..dacd2c0c56 100644 --- a/src/data/selector.ts +++ b/src/data/selector.ts @@ -333,6 +333,7 @@ export interface NumberSelector { mode?: "box" | "slider"; unit_of_measurement?: string; slider_ticks?: boolean; + translation_key?: string; } | null; } From cc3234ad8fd7f9b314e1192dc5d3321be3cb85f5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 4 Jul 2025 22:47:44 +0200 Subject: [PATCH 202/216] Update dependency eslint to v9.30.1 (#26072) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 22174fcd05..8fa8bf2a62 100644 --- a/package.json +++ b/package.json @@ -184,7 +184,7 @@ "babel-plugin-template-html-minifier": "4.1.0", "browserslist-useragent-regexp": "4.1.3", "del": "8.0.0", - "eslint": "9.30.0", + "eslint": "9.30.1", "eslint-config-airbnb-base": "15.0.0", "eslint-config-prettier": "10.1.5", "eslint-import-resolver-webpack": "0.13.10", diff --git a/yarn.lock b/yarn.lock index c3d6c97ada..0f3205bed8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1598,10 +1598,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.30.0": - version: 9.30.0 - resolution: "@eslint/js@npm:9.30.0" - checksum: 10/42e3d5a9cdd5a0842f3ed078e28f81ae1cf04bd2edfd09f43e6dc148bb2e99904f09090007eb6485afd82d837771890c5a8b9ceb1e8c4e256953df4b4aa97308 +"@eslint/js@npm:9.30.1": + version: 9.30.1 + resolution: "@eslint/js@npm:9.30.1" + checksum: 10/f32c8a42b1343b03613b92a4a7c4745d19a93e66ffb8fac18ccd066deaa200b2fc55f476bf0407d7748f663bc947ca78c0518f13f60cd9ed08d0ba8b7bb74370 languageName: node linkType: hard @@ -8124,9 +8124,9 @@ __metadata: languageName: node linkType: hard -"eslint@npm:9.30.0": - version: 9.30.0 - resolution: "eslint@npm:9.30.0" +"eslint@npm:9.30.1": + version: 9.30.1 + resolution: "eslint@npm:9.30.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.12.1" @@ -8134,7 +8134,7 @@ __metadata: "@eslint/config-helpers": "npm:^0.3.0" "@eslint/core": "npm:^0.14.0" "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.30.0" + "@eslint/js": "npm:9.30.1" "@eslint/plugin-kit": "npm:^0.3.1" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" @@ -8170,7 +8170,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10/74c11e6be5997f0de6542932795e997c1586f8f21cdeeda09c89c6c36879a9a593af84f1fd594bd8e22814c54ca0ad65513a0c91b0e8944efb51faed34b7d3b0 + checksum: 10/bd3024f730c88a2d099455f5ec5be2d6293eaf1412eb5111427282e1060b7f87891168ccbded10b71fc7182e05151e42511ec29d4e0d4ece5e3151eaf8d54763 languageName: node linkType: hard @@ -9420,7 +9420,7 @@ __metadata: dialog-polyfill: "npm:0.5.6" echarts: "npm:5.6.0" element-internals-polyfill: "npm:3.0.2" - eslint: "npm:9.30.0" + eslint: "npm:9.30.1" eslint-config-airbnb-base: "npm:15.0.0" eslint-config-prettier: "npm:10.1.5" eslint-import-resolver-webpack: "npm:0.13.10" From e17701210835283f342694aeb2b7b10bc8b975c4 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:34:38 -0700 Subject: [PATCH 203/216] Fix default range icon (#26069) --- src/data/icons.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/data/icons.ts b/src/data/icons.ts index 4c6699c038..c42b8e0f21 100644 --- a/src/data/icons.ts +++ b/src/data/icons.ts @@ -354,7 +354,10 @@ const getIconFromTranslations = ( } // Then check for range-based icons if we have a numeric state if (state !== undefined && translations.range && !isNaN(Number(state))) { - return getIconFromRange(Number(state), translations.range); + return ( + getIconFromRange(Number(state), translations.range) ?? + translations.default + ); } // Fallback to default icon return translations.default; @@ -518,7 +521,10 @@ export const domainIcon = async ( } // Then check for range-based icons if we have a numeric state if (state !== undefined && translations.range && !isNaN(Number(state))) { - return getIconFromRange(Number(state), translations.range); + return ( + getIconFromRange(Number(state), translations.range) ?? + translations.default + ); } // Fallback to default icon return translations.default; From a385655c856f8d1e8a4d01f3b950ffbdaacf64cd Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Sat, 5 Jul 2025 09:27:41 +0300 Subject: [PATCH 204/216] Remove deprecated dependency @types/glob (#26075) --- package.json | 1 - yarn.lock | 18 ------------------ 2 files changed, 19 deletions(-) diff --git a/package.json b/package.json index 8fa8bf2a62..1471b94883 100644 --- a/package.json +++ b/package.json @@ -165,7 +165,6 @@ "@types/chromecast-caf-receiver": "6.0.22", "@types/chromecast-caf-sender": "1.0.11", "@types/color-name": "2.0.0", - "@types/glob": "8.1.0", "@types/html-minifier-terser": "7.0.2", "@types/js-yaml": "4.0.9", "@types/leaflet": "1.9.19", diff --git a/yarn.lock b/yarn.lock index 0f3205bed8..426c6aa5b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4577,16 +4577,6 @@ __metadata: languageName: node linkType: hard -"@types/glob@npm:8.1.0": - version: 8.1.0 - resolution: "@types/glob@npm:8.1.0" - dependencies: - "@types/minimatch": "npm:^5.1.2" - "@types/node": "npm:*" - checksum: 10/9101f3a9061e40137190f70626aa0e202369b5ec4012c3fabe6f5d229cce04772db9a94fa5a0eb39655e2e4ad105c38afbb4af56a56c0996a8c7d4fc72350e3d - languageName: node - linkType: hard - "@types/hammerjs@npm:^2.0.36": version: 2.0.46 resolution: "@types/hammerjs@npm:2.0.46" @@ -4720,13 +4710,6 @@ __metadata: languageName: node linkType: hard -"@types/minimatch@npm:^5.1.2": - version: 5.1.2 - resolution: "@types/minimatch@npm:5.1.2" - checksum: 10/94db5060d20df2b80d77b74dd384df3115f01889b5b6c40fa2dfa27cfc03a68fb0ff7c1f2a0366070263eb2e9d6bfd8c87111d4bc3ae93c3f291297c1bf56c85 - languageName: node - linkType: hard - "@types/mocha@npm:10.0.10": version: 10.0.10 resolution: "@types/mocha@npm:10.0.10" @@ -9382,7 +9365,6 @@ __metadata: "@types/chromecast-caf-receiver": "npm:6.0.22" "@types/chromecast-caf-sender": "npm:1.0.11" "@types/color-name": "npm:2.0.0" - "@types/glob": "npm:8.1.0" "@types/html-minifier-terser": "npm:7.0.2" "@types/js-yaml": "npm:4.0.9" "@types/leaflet": "npm:1.9.19" From 3835912b01fc3b8112454112b2e262c7a0dbea07 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 12:14:01 +0200 Subject: [PATCH 205/216] Update dependency @rsdoctor/rspack-plugin to v1.1.6 (#26078) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 94 ++++++++++++++++++++++++++-------------------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 1471b94883..dff79291f8 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,7 @@ "@octokit/auth-oauth-device": "8.0.1", "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", - "@rsdoctor/rspack-plugin": "1.1.5", + "@rsdoctor/rspack-plugin": "1.1.6", "@rspack/cli": "1.4.2", "@rspack/core": "1.4.2", "@types/babel__plugin-transform-runtime": "7.9.5", diff --git a/yarn.lock b/yarn.lock index 426c6aa5b9..201a215abb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3804,22 +3804,22 @@ __metadata: languageName: node linkType: hard -"@rsdoctor/client@npm:1.1.5": - version: 1.1.5 - resolution: "@rsdoctor/client@npm:1.1.5" - checksum: 10/fa157b12647a668d01452eba112f5c1cf338d6ca6b2b201a95f6275a42b6acde068fc70b7e0990961ab04ef77c2ae9278e37076bfa78c0c449b6ded8b716b06b +"@rsdoctor/client@npm:1.1.6": + version: 1.1.6 + resolution: "@rsdoctor/client@npm:1.1.6" + checksum: 10/979c05ee26cf9082fd893f5794f3339a9032b7a1495fef0ab3aa83272ad2fff615107e89a2ed54b791a113620ecb5533b406c91c80ae215fa9bd32e26a9516ed languageName: node linkType: hard -"@rsdoctor/core@npm:1.1.5": - version: 1.1.5 - resolution: "@rsdoctor/core@npm:1.1.5" +"@rsdoctor/core@npm:1.1.6": + version: 1.1.6 + resolution: "@rsdoctor/core@npm:1.1.6" dependencies: "@rsbuild/plugin-check-syntax": "npm:1.3.0" - "@rsdoctor/graph": "npm:1.1.5" - "@rsdoctor/sdk": "npm:1.1.5" - "@rsdoctor/types": "npm:1.1.5" - "@rsdoctor/utils": "npm:1.1.5" + "@rsdoctor/graph": "npm:1.1.6" + "@rsdoctor/sdk": "npm:1.1.6" + "@rsdoctor/types": "npm:1.1.6" + "@rsdoctor/utils": "npm:1.1.6" axios: "npm:^1.10.0" browserslist-load-config: "npm:^1.0.0" enhanced-resolve: "npm:5.12.0" @@ -3830,50 +3830,50 @@ __metadata: semver: "npm:^7.7.2" source-map: "npm:^0.7.4" webpack-bundle-analyzer: "npm:^4.10.2" - checksum: 10/740a52256d027114d798d997450be502673d976f215ff03b41aafc422c71d62a2c9ff419332ed6865dc9e6fe37f7c4469e19ce571745417fa2b19060b70079a6 + checksum: 10/04643bf79cfe44085baa506f7532c57caf468ef6f4fa58a9a93ae297f03b550092a0590b5ad93bce3bab8cedfbbb3ae8f3d151d67440c17d2cf1a350df872783 languageName: node linkType: hard -"@rsdoctor/graph@npm:1.1.5": - version: 1.1.5 - resolution: "@rsdoctor/graph@npm:1.1.5" +"@rsdoctor/graph@npm:1.1.6": + version: 1.1.6 + resolution: "@rsdoctor/graph@npm:1.1.6" dependencies: - "@rsdoctor/types": "npm:1.1.5" - "@rsdoctor/utils": "npm:1.1.5" + "@rsdoctor/types": "npm:1.1.6" + "@rsdoctor/utils": "npm:1.1.6" lodash.unionby: "npm:^4.8.0" socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" - checksum: 10/ce6eec3d82ea6717d9d2c2f9e6af9e9e75876814d85e50bbfc5747a4842f275bb10eea035ffbf60c2de1d1273f505a18e7d4111df8acfb0a386eedcf1d65cd1b + checksum: 10/ff3eb5191e39ba7c53fcab11423a23bd7c74c9342a8aa23e3134d7f451fd31ac8d6bb6af9b18728e16dfa5d3f46933cf1d56c3828a1b8c4664eb2353be7f9c71 languageName: node linkType: hard -"@rsdoctor/rspack-plugin@npm:1.1.5": - version: 1.1.5 - resolution: "@rsdoctor/rspack-plugin@npm:1.1.5" +"@rsdoctor/rspack-plugin@npm:1.1.6": + version: 1.1.6 + resolution: "@rsdoctor/rspack-plugin@npm:1.1.6" dependencies: - "@rsdoctor/core": "npm:1.1.5" - "@rsdoctor/graph": "npm:1.1.5" - "@rsdoctor/sdk": "npm:1.1.5" - "@rsdoctor/types": "npm:1.1.5" - "@rsdoctor/utils": "npm:1.1.5" + "@rsdoctor/core": "npm:1.1.6" + "@rsdoctor/graph": "npm:1.1.6" + "@rsdoctor/sdk": "npm:1.1.6" + "@rsdoctor/types": "npm:1.1.6" + "@rsdoctor/utils": "npm:1.1.6" lodash: "npm:^4.17.21" peerDependencies: "@rspack/core": "*" peerDependenciesMeta: "@rspack/core": optional: true - checksum: 10/8265504174d21d3682ce7a015dfcaa11f460b6c3723118c380895014a69b08246b771cbb67a4102797a43f05398b760e82512939a9c1963ab7bbf376c3efd6e0 + checksum: 10/c3fee7db4bd25b63638d43b272c3df68c44b96f3d31b33e4501c8ea47860d9e7506cee2a493090e99c5beca1fced218fb390ddcc6092da1e47440fa7bc28be72 languageName: node linkType: hard -"@rsdoctor/sdk@npm:1.1.5": - version: 1.1.5 - resolution: "@rsdoctor/sdk@npm:1.1.5" +"@rsdoctor/sdk@npm:1.1.6": + version: 1.1.6 + resolution: "@rsdoctor/sdk@npm:1.1.6" dependencies: - "@rsdoctor/client": "npm:1.1.5" - "@rsdoctor/graph": "npm:1.1.5" - "@rsdoctor/types": "npm:1.1.5" - "@rsdoctor/utils": "npm:1.1.5" + "@rsdoctor/client": "npm:1.1.6" + "@rsdoctor/graph": "npm:1.1.6" + "@rsdoctor/types": "npm:1.1.6" + "@rsdoctor/utils": "npm:1.1.6" "@types/fs-extra": "npm:^11.0.4" body-parser: "npm:1.20.3" cors: "npm:2.8.5" @@ -3886,13 +3886,13 @@ __metadata: socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" tapable: "npm:2.2.2" - checksum: 10/bf14b497961c5bc79cea4f3242829611a36907e0c215bc857dad2c0db9efe389dc755987936ea45b41c7a229160a89ff689ab70021da3fb780a69e748354baab + checksum: 10/7e38f4a1d6034d6a4d4ac0173757e967284290e256b74a2d3cb92a360169bea85f0f0f15c2f21a328f5460ab355ba5de213deada70794fd4f50ade096df481ad languageName: node linkType: hard -"@rsdoctor/types@npm:1.1.5": - version: 1.1.5 - resolution: "@rsdoctor/types@npm:1.1.5" +"@rsdoctor/types@npm:1.1.6": + version: 1.1.6 + resolution: "@rsdoctor/types@npm:1.1.6" dependencies: "@types/connect": "npm:3.4.38" "@types/estree": "npm:1.0.5" @@ -3906,16 +3906,16 @@ __metadata: optional: true webpack: optional: true - checksum: 10/5791f053503c1cf811938d3273fcc1991fc7d6b387af7a3309e690be4bec102a0603cf24775ace70420671be9a107b2cca98904f045d98f808354f64413d6bdd + checksum: 10/cca6a83b22126bfc99c4c8428ee2a85dac7dc41f98307ee14019b2418e11197c0a79447d32f521e9d045aec9ca2829740935a865bb292db9b7b925da75bd0ea2 languageName: node linkType: hard -"@rsdoctor/utils@npm:1.1.5": - version: 1.1.5 - resolution: "@rsdoctor/utils@npm:1.1.5" +"@rsdoctor/utils@npm:1.1.6": + version: 1.1.6 + resolution: "@rsdoctor/utils@npm:1.1.6" dependencies: "@babel/code-frame": "npm:7.26.2" - "@rsdoctor/types": "npm:1.1.5" + "@rsdoctor/types": "npm:1.1.6" "@types/estree": "npm:1.0.5" acorn: "npm:^8.10.0" acorn-import-attributes: "npm:^1.9.5" @@ -3929,9 +3929,9 @@ __metadata: json-stream-stringify: "npm:3.0.1" lines-and-columns: "npm:2.0.4" picocolors: "npm:^1.1.1" - rslog: "npm:^1.2.7" + rslog: "npm:^1.2.8" strip-ansi: "npm:^6.0.1" - checksum: 10/4b128c455bf2246b48243517e752fb01fa9495df6a7a98fb595c3cc369384f71513f0ec50ee83c8703c95c40f303839b4fe533bc86f3dd65aee71c2c3ad0beb0 + checksum: 10/6c8e9a3ba5b8891892dfe65508b78dfce8b4fb7e9a2d66051b51739910cf76bb0a0864966ac20dd1ddb2dc4b3676c0201171ae38dcefdfda7b38bf14e8c930da languageName: node linkType: hard @@ -9353,7 +9353,7 @@ __metadata: "@octokit/plugin-retry": "npm:8.0.1" "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" - "@rsdoctor/rspack-plugin": "npm:1.1.5" + "@rsdoctor/rspack-plugin": "npm:1.1.6" "@rspack/cli": "npm:1.4.2" "@rspack/core": "npm:1.4.2" "@shoelace-style/shoelace": "npm:2.20.1" @@ -12936,7 +12936,7 @@ __metadata: languageName: node linkType: hard -"rslog@npm:^1.2.7": +"rslog@npm:^1.2.8": version: 1.2.9 resolution: "rslog@npm:1.2.9" checksum: 10/f8c1d890049671aa73fa9c5682befd756c76fcfe885841ca3d6e1167edf1798a890dd57fb94a1493de7c7d28b41a9e42119dd9a483495acaa666ac04f149ee86 From 5760614b6545424adbd39e014043e4aeb2cd0e6c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 12:17:24 +0200 Subject: [PATCH 206/216] Update babel monorepo to v7.28.0 (#26079) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 6 +- yarn.lock | 286 +++++++++++++++++++++++++++------------------------ 2 files changed, 153 insertions(+), 139 deletions(-) diff --git a/package.json b/package.json index dff79291f8..9ebeed5086 100644 --- a/package.json +++ b/package.json @@ -149,10 +149,10 @@ "xss": "1.0.15" }, "devDependencies": { - "@babel/core": "7.27.7", + "@babel/core": "7.28.0", "@babel/helper-define-polyfill-provider": "0.6.5", - "@babel/plugin-transform-runtime": "7.27.4", - "@babel/preset-env": "7.27.2", + "@babel/plugin-transform-runtime": "7.28.0", + "@babel/preset-env": "7.28.0", "@bundle-stats/plugin-webpack-filter": "4.21.0", "@lokalise/node-api": "14.9.0", "@octokit/auth-oauth-device": "8.0.1", diff --git a/yarn.lock b/yarn.lock index 201a215abb..6869a11a0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,50 +63,50 @@ __metadata: languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.27.2": - version: 7.27.5 - resolution: "@babel/compat-data@npm:7.27.5" - checksum: 10/04c343b8a25955bbbe1569564c63ac481a74710eb2e7989b97bd10baf2f0f3b1aa1b6c6122749806e92d70cfc22c10c757ff62336eb10a28ea98ab2b82bc0c2c +"@babel/compat-data@npm:^7.27.2, @babel/compat-data@npm:^7.27.7, @babel/compat-data@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/compat-data@npm:7.28.0" + checksum: 10/1a56a5e48c7259f72cc4329adeca38e72fd650ea09de267ea4aa070e3da91e5c265313b6656823fff77d64a8bab9554f276c66dade9355fdc0d8604deea015aa languageName: node linkType: hard -"@babel/core@npm:7.27.7, @babel/core@npm:^7.24.4": - version: 7.27.7 - resolution: "@babel/core@npm:7.27.7" +"@babel/core@npm:7.28.0, @babel/core@npm:^7.24.4": + version: 7.28.0 + resolution: "@babel/core@npm:7.28.0" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.27.1" - "@babel/generator": "npm:^7.27.5" + "@babel/generator": "npm:^7.28.0" "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-module-transforms": "npm:^7.27.3" "@babel/helpers": "npm:^7.27.6" - "@babel/parser": "npm:^7.27.7" + "@babel/parser": "npm:^7.28.0" "@babel/template": "npm:^7.27.2" - "@babel/traverse": "npm:^7.27.7" - "@babel/types": "npm:^7.27.7" + "@babel/traverse": "npm:^7.28.0" + "@babel/types": "npm:^7.28.0" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 10/3503d575ebbf6e66d43d17bbf14c7f93466e8f44ba6f566722747ae887d6c3890ecf64447a3bae8e431ea96907180ac8618b5452d85d9951f571116122b7f66d + checksum: 10/1c86eec8d76053f7b1c5f65296d51d7b8ac00f80d169ff76d3cd2e7d85ab222eb100d40cc3314f41b96c8cc06e9abab21c63d246161f0f3f70ef14c958419c33 languageName: node linkType: hard -"@babel/generator@npm:^7.27.5": - version: 7.27.5 - resolution: "@babel/generator@npm:7.27.5" +"@babel/generator@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/generator@npm:7.28.0" dependencies: - "@babel/parser": "npm:^7.27.5" - "@babel/types": "npm:^7.27.3" - "@jridgewell/gen-mapping": "npm:^0.3.5" - "@jridgewell/trace-mapping": "npm:^0.3.25" + "@babel/parser": "npm:^7.28.0" + "@babel/types": "npm:^7.28.0" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" jsesc: "npm:^3.0.2" - checksum: 10/f5e6942670cb32156b3ac2d75ce09b373558823387f15dd1413c27fe9eb5756a7c6011fc7f956c7acc53efb530bfb28afffa24364d46c4e9ffccc4e5c8b3b094 + checksum: 10/064c5ba4c07ecd7600377bd0022d5f6bdb3b35e9ff78d9378f6bd1e656467ca902c091647222ab2f0d2967f6d6c0ca33157d37dd9b1c51926c9b0e1527ab9b92 languageName: node linkType: hard -"@babel/helper-annotate-as-pure@npm:^7.27.1": +"@babel/helper-annotate-as-pure@npm:^7.27.1, @babel/helper-annotate-as-pure@npm:^7.27.3": version: 7.27.3 resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" dependencies: @@ -158,7 +158,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-define-polyfill-provider@npm:0.6.5, @babel/helper-define-polyfill-provider@npm:^0.6.3, @babel/helper-define-polyfill-provider@npm:^0.6.4": +"@babel/helper-define-polyfill-provider@npm:0.6.5, @babel/helper-define-polyfill-provider@npm:^0.6.5": version: 0.6.5 resolution: "@babel/helper-define-polyfill-provider@npm:0.6.5" dependencies: @@ -173,6 +173,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-globals@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/helper-globals@npm:7.28.0" + checksum: 10/91445f7edfde9b65dcac47f4f858f68dc1661bf73332060ab67ad7cc7b313421099a2bfc4bda30c3db3842cfa1e86fffbb0d7b2c5205a177d91b22c8d7d9cb47 + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-member-expression-to-functions@npm:7.27.1" @@ -300,14 +307,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.5, @babel/parser@npm:^7.27.7": - version: 7.27.7 - resolution: "@babel/parser@npm:7.27.7" +"@babel/parser@npm:^7.23.5, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/parser@npm:7.28.0" dependencies: - "@babel/types": "npm:^7.27.7" + "@babel/types": "npm:^7.28.0" bin: parser: ./bin/babel-parser.js - checksum: 10/ed25ccfc709e77b94afebfa8377cca2ee5d0750162a6b4e7eb7b679ccdf307d1a015dee58d94afe726ed6d278a83aa348cb3a47717222ac4c3650d077f6ca4fd + checksum: 10/2c14a0d2600bae9ab81924df0a85bbd34e427caa099c260743f7c6c12b2042e743e776043a0d1a2573229ae648f7e66a80cfb26fc27e2a9eb59b55932d44c817 languageName: node linkType: hard @@ -424,16 +431,16 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.27.1" +"@babel/plugin-transform-async-generator-functions@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.28.0" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" "@babel/helper-remap-async-to-generator": "npm:^7.27.1" - "@babel/traverse": "npm:^7.27.1" + "@babel/traverse": "npm:^7.28.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/92e8ba589e8b128255846375e13fee30a3b77c889578f1f30da57ee26133f397dbbc81b27e1f19c12080b096930e62bce1dcbaa7a1453d296f51eb8bda3b8d39 + checksum: 10/8ad31b9969b203dec572738a872e17b14ef76ca45b4ef5ffa76f3514be417ca233d1a0978e5f8de166412a8a745619eb22b07cc5df96f5ebad8ca500f920f61b languageName: node linkType: hard @@ -461,14 +468,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.27.1": - version: 7.27.5 - resolution: "@babel/plugin-transform-block-scoping@npm:7.27.5" +"@babel/plugin-transform-block-scoping@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-block-scoping@npm:7.28.0" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/bc911f0aa15bc9a5e0e1130681c1a6abd05300f6c8c02af9c97b0eaaae43b0f2936b34a5efc1a166a8e296c421c574a0e04dd0d6dc62adaab1246a387e6cfe26 + checksum: 10/eefa0d0b3cd8005b77ad3239700cec90c2b19612e664772c50da6b917b272d20ebc831db6ff0d9fef011a810d9f02c434fdf551b3a4264eb834afa20090a9434 languageName: node linkType: hard @@ -496,19 +503,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/plugin-transform-classes@npm:7.27.1" +"@babel/plugin-transform-classes@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-classes@npm:7.28.0" dependencies: - "@babel/helper-annotate-as-pure": "npm:^7.27.1" - "@babel/helper-compilation-targets": "npm:^7.27.1" + "@babel/helper-annotate-as-pure": "npm:^7.27.3" + "@babel/helper-compilation-targets": "npm:^7.27.2" + "@babel/helper-globals": "npm:^7.28.0" "@babel/helper-plugin-utils": "npm:^7.27.1" "@babel/helper-replace-supers": "npm:^7.27.1" - "@babel/traverse": "npm:^7.27.1" - globals: "npm:^11.1.0" + "@babel/traverse": "npm:^7.28.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/4ac2224fa68b933c80b4755300d795e055f6fb18c51432e9a4c048edcd6c64cae097eb9063d25f6c7e706ecd85a4c0b89b6f89b320b5798e3139c9cc4ff99f61 + checksum: 10/1a812a02f641ffc80b139b3c690ceba52476576f9df1a62dbdde9c412e88ca143b7b872da71665838c34276c4ed92f6547199044a424222b84f9a8ee7c85798f languageName: node linkType: hard @@ -524,14 +531,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-destructuring@npm:^7.27.1, @babel/plugin-transform-destructuring@npm:^7.27.3": - version: 7.27.3 - resolution: "@babel/plugin-transform-destructuring@npm:7.27.3" +"@babel/plugin-transform-destructuring@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-destructuring@npm:7.28.0" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" + "@babel/traverse": "npm:^7.28.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/d5b1868d079551c0a2e923419613efe18a987548219bb378c61ab7e005d4f3ea590067f93996df6d896177c1cae1396b4aae9163c8a4ee77e9ffbc11a78fb88d + checksum: 10/cddab2520ff32d18005670fc6646396a253d3811d1ccc49f6f858469f05985ee896c346a0cb34d1cf25155c9be76d1068ff878cf8e8459bd3fa27513ec5a6802 languageName: node linkType: hard @@ -581,6 +589,18 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-explicit-resource-management@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-explicit-resource-management@npm:7.28.0" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.27.1" + "@babel/plugin-transform-destructuring": "npm:^7.28.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10/93d7835160bf8623c7b7072898046c9a2a46cf911f38fa2a002de40a11045a65bf9c1663c98f2e4e04615037f63391832c20b45d7bc26a16d39a97995d0669bc + languageName: node + linkType: hard + "@babel/plugin-transform-exponentiation-operator@npm:^7.27.1": version: 7.27.1 resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.27.1" @@ -767,17 +787,18 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.27.2": - version: 7.27.3 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.27.3" +"@babel/plugin-transform-object-rest-spread@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.28.0" dependencies: "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-plugin-utils": "npm:^7.27.1" - "@babel/plugin-transform-destructuring": "npm:^7.27.3" - "@babel/plugin-transform-parameters": "npm:^7.27.1" + "@babel/plugin-transform-destructuring": "npm:^7.28.0" + "@babel/plugin-transform-parameters": "npm:^7.27.7" + "@babel/traverse": "npm:^7.28.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/7cc7be29a99010aac04fd78383f06d550b26460ea5367489e58ae484f0ed2f176966f0196bea0c2114a9872dd854a482bca38a9fad661c9d10d102c7195d53fd + checksum: 10/55d37dbc0d5d47db860b7cc9fe5e3660d83108113fc3f2a7daecb95c20d4046a70247777969006f7db8fb2005eeeda719b9ff21e9f6d43355d0a62fc41b5880e languageName: node linkType: hard @@ -816,14 +837,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-parameters@npm:^7.27.1": - version: 7.27.1 - resolution: "@babel/plugin-transform-parameters@npm:7.27.1" +"@babel/plugin-transform-parameters@npm:^7.27.7": + version: 7.27.7 + resolution: "@babel/plugin-transform-parameters@npm:7.27.7" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/47db574f8f3adf7a5d85933c9a2a2dee956ceda9e00fb4e03e9a9d600b559f06cba2da7c5e78a12b05dcf993cf147634edf0391f3f20a6b451830f41be47fe68 + checksum: 10/ba0aa8c977a03bf83030668f64c1d721e4e82d8cce89cdde75a2755862b79dbe9e7f58ca955e68c721fd494d6ee3826e46efad3fbf0855fcc92cb269477b4777 languageName: node linkType: hard @@ -863,14 +884,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-regenerator@npm:^7.27.1": - version: 7.27.5 - resolution: "@babel/plugin-transform-regenerator@npm:7.27.5" +"@babel/plugin-transform-regenerator@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-regenerator@npm:7.28.0" dependencies: "@babel/helper-plugin-utils": "npm:^7.27.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/ae4e203df1cb44418001fc0f5c75d7079ab342a1d629d6c0f581a3e521d0f6e5f7d5b351cb009e396782db579b29ceb66f260a873e0b8cd4c6901449af7edaa2 + checksum: 10/f8d4e635857b32b7ff8eeff0726e9bbfbece12eccd65e53d081fe0176cb432cd6bfcc64d28edc34c3cfa1aa79da46ec8d0b9b4f9242da7ec2153c34ea6d2163c languageName: node linkType: hard @@ -897,19 +918,19 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-runtime@npm:7.27.4": - version: 7.27.4 - resolution: "@babel/plugin-transform-runtime@npm:7.27.4" +"@babel/plugin-transform-runtime@npm:7.28.0": + version: 7.28.0 + resolution: "@babel/plugin-transform-runtime@npm:7.28.0" dependencies: "@babel/helper-module-imports": "npm:^7.27.1" "@babel/helper-plugin-utils": "npm:^7.27.1" - babel-plugin-polyfill-corejs2: "npm:^0.4.10" - babel-plugin-polyfill-corejs3: "npm:^0.11.0" - babel-plugin-polyfill-regenerator: "npm:^0.6.1" + babel-plugin-polyfill-corejs2: "npm:^0.4.14" + babel-plugin-polyfill-corejs3: "npm:^0.13.0" + babel-plugin-polyfill-regenerator: "npm:^0.6.5" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/418922fe95ea09efb967a3d842a75f6ab2503e570fb705bd2f7195f45f8a60269da31affd58b4e91f5fb80fb14cede47da34bc60f1a080a5baf7484ebe261a55 + checksum: 10/43abe94e64ab4d2be71958d1ae0dbfeeb241ce820e4c48f285c8bcc2e764b673786f784bc743b6903bfc72ec694003f1e5c2b032b83accd591dfc203a64e3d4a languageName: node linkType: hard @@ -1016,11 +1037,11 @@ __metadata: languageName: node linkType: hard -"@babel/preset-env@npm:7.27.2, @babel/preset-env@npm:^7.11.0": - version: 7.27.2 - resolution: "@babel/preset-env@npm:7.27.2" +"@babel/preset-env@npm:7.28.0, @babel/preset-env@npm:^7.11.0": + version: 7.28.0 + resolution: "@babel/preset-env@npm:7.28.0" dependencies: - "@babel/compat-data": "npm:^7.27.2" + "@babel/compat-data": "npm:^7.28.0" "@babel/helper-compilation-targets": "npm:^7.27.2" "@babel/helper-plugin-utils": "npm:^7.27.1" "@babel/helper-validator-option": "npm:^7.27.1" @@ -1034,19 +1055,20 @@ __metadata: "@babel/plugin-syntax-import-attributes": "npm:^7.27.1" "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" "@babel/plugin-transform-arrow-functions": "npm:^7.27.1" - "@babel/plugin-transform-async-generator-functions": "npm:^7.27.1" + "@babel/plugin-transform-async-generator-functions": "npm:^7.28.0" "@babel/plugin-transform-async-to-generator": "npm:^7.27.1" "@babel/plugin-transform-block-scoped-functions": "npm:^7.27.1" - "@babel/plugin-transform-block-scoping": "npm:^7.27.1" + "@babel/plugin-transform-block-scoping": "npm:^7.28.0" "@babel/plugin-transform-class-properties": "npm:^7.27.1" "@babel/plugin-transform-class-static-block": "npm:^7.27.1" - "@babel/plugin-transform-classes": "npm:^7.27.1" + "@babel/plugin-transform-classes": "npm:^7.28.0" "@babel/plugin-transform-computed-properties": "npm:^7.27.1" - "@babel/plugin-transform-destructuring": "npm:^7.27.1" + "@babel/plugin-transform-destructuring": "npm:^7.28.0" "@babel/plugin-transform-dotall-regex": "npm:^7.27.1" "@babel/plugin-transform-duplicate-keys": "npm:^7.27.1" "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "npm:^7.27.1" "@babel/plugin-transform-dynamic-import": "npm:^7.27.1" + "@babel/plugin-transform-explicit-resource-management": "npm:^7.28.0" "@babel/plugin-transform-exponentiation-operator": "npm:^7.27.1" "@babel/plugin-transform-export-namespace-from": "npm:^7.27.1" "@babel/plugin-transform-for-of": "npm:^7.27.1" @@ -1063,15 +1085,15 @@ __metadata: "@babel/plugin-transform-new-target": "npm:^7.27.1" "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.27.1" "@babel/plugin-transform-numeric-separator": "npm:^7.27.1" - "@babel/plugin-transform-object-rest-spread": "npm:^7.27.2" + "@babel/plugin-transform-object-rest-spread": "npm:^7.28.0" "@babel/plugin-transform-object-super": "npm:^7.27.1" "@babel/plugin-transform-optional-catch-binding": "npm:^7.27.1" "@babel/plugin-transform-optional-chaining": "npm:^7.27.1" - "@babel/plugin-transform-parameters": "npm:^7.27.1" + "@babel/plugin-transform-parameters": "npm:^7.27.7" "@babel/plugin-transform-private-methods": "npm:^7.27.1" "@babel/plugin-transform-private-property-in-object": "npm:^7.27.1" "@babel/plugin-transform-property-literals": "npm:^7.27.1" - "@babel/plugin-transform-regenerator": "npm:^7.27.1" + "@babel/plugin-transform-regenerator": "npm:^7.28.0" "@babel/plugin-transform-regexp-modifiers": "npm:^7.27.1" "@babel/plugin-transform-reserved-words": "npm:^7.27.1" "@babel/plugin-transform-shorthand-properties": "npm:^7.27.1" @@ -1084,14 +1106,14 @@ __metadata: "@babel/plugin-transform-unicode-regex": "npm:^7.27.1" "@babel/plugin-transform-unicode-sets-regex": "npm:^7.27.1" "@babel/preset-modules": "npm:0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2: "npm:^0.4.10" - babel-plugin-polyfill-corejs3: "npm:^0.11.0" - babel-plugin-polyfill-regenerator: "npm:^0.6.1" - core-js-compat: "npm:^3.40.0" + babel-plugin-polyfill-corejs2: "npm:^0.4.14" + babel-plugin-polyfill-corejs3: "npm:^0.13.0" + babel-plugin-polyfill-regenerator: "npm:^0.6.5" + core-js-compat: "npm:^3.43.0" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 10/3748b5e5582bee12f2b21ee4af9552a0ea8851fdfa8e54cdab142ac9191b7e9b1673d23056c0d2c3c6fd554eb85873664acfc9829c4f14a8ae7676548184eff6 + checksum: 10/8814453ffe4cfd5926cf2af0ecc956240bcc1e5f49592015962a5f1c115c5c0c34c1e0a5c66d3d4e1a283644bb5ea4e199ced0b6117ffd20113a994fd3080798 languageName: node linkType: hard @@ -1126,28 +1148,28 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.27.7": - version: 7.27.7 - resolution: "@babel/traverse@npm:7.27.7" +"@babel/traverse@npm:^7.27.1, @babel/traverse@npm:^7.27.3, @babel/traverse@npm:^7.28.0": + version: 7.28.0 + resolution: "@babel/traverse@npm:7.28.0" dependencies: "@babel/code-frame": "npm:^7.27.1" - "@babel/generator": "npm:^7.27.5" - "@babel/parser": "npm:^7.27.7" + "@babel/generator": "npm:^7.28.0" + "@babel/helper-globals": "npm:^7.28.0" + "@babel/parser": "npm:^7.28.0" "@babel/template": "npm:^7.27.2" - "@babel/types": "npm:^7.27.7" + "@babel/types": "npm:^7.28.0" debug: "npm:^4.3.1" - globals: "npm:^11.1.0" - checksum: 10/10b83c362b5c2758dbbf308c3144fa0fdcc98c8f107c2b7637e2c3c975f8b4e77a18e4b5854200f5ca3749ec3bcabd57bb9831ae8455f0701cabc6366983f379 + checksum: 10/c1c24b12b6cb46241ec5d11ddbd2989d6955c282715cbd8ee91a09fe156b3bdb0b88353ac33329c2992113e3dfb5198f616c834f8805bb3fa85da1f864bec5f3 languageName: node linkType: hard -"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6, @babel/types@npm:^7.27.7, @babel/types@npm:^7.4.4": - version: 7.27.7 - resolution: "@babel/types@npm:7.27.7" +"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.27.6, @babel/types@npm:^7.28.0, @babel/types@npm:^7.4.4": + version: 7.28.0 + resolution: "@babel/types@npm:7.28.0" dependencies: "@babel/helper-string-parser": "npm:^7.27.1" "@babel/helper-validator-identifier": "npm:^7.27.1" - checksum: 10/39e9f05527ef0771dfb6220213a9ef2ca35c2b6d531e3310c8ffafb53aa50362e809f75af8feb28bd6abb874a00c02b05ac00e3063ee239db5c6f1653eab19c5 + checksum: 10/2f28b84efb5005d1e85fc3944219c284400c42aeefc1f6e10500a74fed43b3dfb4f9e349a5d6e0e3fc24f5d241c513b30ef00ede2885535ce7a0a4e111c2098e languageName: node linkType: hard @@ -2097,14 +2119,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.5": - version: 0.3.8 - resolution: "@jridgewell/gen-mapping@npm:0.3.8" +"@jridgewell/gen-mapping@npm:^0.3.12, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.12 + resolution: "@jridgewell/gen-mapping@npm:0.3.12" dependencies: - "@jridgewell/set-array": "npm:^1.2.1" - "@jridgewell/sourcemap-codec": "npm:^1.4.10" + "@jridgewell/sourcemap-codec": "npm:^1.5.0" "@jridgewell/trace-mapping": "npm:^0.3.24" - checksum: 10/9d3a56ab3612ab9b85d38b2a93b87f3324f11c5130859957f6500e4ac8ce35f299d5ccc3ecd1ae87597601ecf83cee29e9afd04c18777c24011073992ff946df + checksum: 10/151667531566417a940d4dd0a319724979f7a90b9deb9f1617344e1183887d78c835bc1a9209c1ee10fc8a669cdd7ac8120a43a2b6bc8d0d5dd18a173059ff4b languageName: node linkType: hard @@ -2115,13 +2136,6 @@ __metadata: languageName: node linkType: hard -"@jridgewell/set-array@npm:^1.2.1": - version: 1.2.1 - resolution: "@jridgewell/set-array@npm:1.2.1" - checksum: 10/832e513a85a588f8ed4f27d1279420d8547743cc37fcad5a5a76fc74bb895b013dfe614d0eed9cb860048e6546b798f8f2652020b4b2ba0561b05caa8c654b10 - languageName: node - linkType: hard - "@jridgewell/source-map@npm:^0.3.3": version: 0.3.6 resolution: "@jridgewell/source-map@npm:0.3.6" @@ -2132,20 +2146,20 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": +"@jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.5.0": version: 1.5.0 resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" checksum: 10/4ed6123217569a1484419ac53f6ea0d9f3b57e5b57ab30d7c267bdb27792a27eb0e4b08e84a2680aa55cc2f2b411ffd6ec3db01c44fdc6dc43aca4b55f8374fd languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": - version: 0.3.25 - resolution: "@jridgewell/trace-mapping@npm:0.3.25" +"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25, @jridgewell/trace-mapping@npm:^0.3.28": + version: 0.3.29 + resolution: "@jridgewell/trace-mapping@npm:0.3.29" dependencies: "@jridgewell/resolve-uri": "npm:^3.1.0" "@jridgewell/sourcemap-codec": "npm:^1.4.14" - checksum: 10/dced32160a44b49d531b80a4a2159dceab6b3ddf0c8e95a0deae4b0e894b172defa63d5ac52a19c2068e1fe7d31ea4ba931fbeec103233ecb4208953967120fc + checksum: 10/64e1ce0dc3a9e56b0118eaf1b2f50746fd59a36de37516cc6855b5370d5f367aa8229e1237536d738262e252c70ee229619cb04e3f3b822146ee3eb1b7ab297f languageName: node linkType: hard @@ -6044,39 +6058,39 @@ __metadata: languageName: node linkType: hard -"babel-plugin-polyfill-corejs2@npm:^0.4.10": - version: 0.4.13 - resolution: "babel-plugin-polyfill-corejs2@npm:0.4.13" +"babel-plugin-polyfill-corejs2@npm:^0.4.14": + version: 0.4.14 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.14" dependencies: - "@babel/compat-data": "npm:^7.22.6" - "@babel/helper-define-polyfill-provider": "npm:^0.6.4" + "@babel/compat-data": "npm:^7.27.7" + "@babel/helper-define-polyfill-provider": "npm:^0.6.5" semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/e238534f345edb26471438cdef8f9182892c4a857fc1cd74d8ecb3072d5126232e299d3850027cecbcb599e721cef835b9e63aba35c2db41733635d39b76c1d8 + checksum: 10/8ec00a1b821ccbfcc432630da66e98bc417f5301f4ce665269d50d245a18ad3ce8a8af2a007f28e3defcd555bb8ce65f16b0d4b6d131bd788e2b97d8b8953332 languageName: node linkType: hard -"babel-plugin-polyfill-corejs3@npm:^0.11.0": - version: 0.11.1 - resolution: "babel-plugin-polyfill-corejs3@npm:0.11.1" +"babel-plugin-polyfill-corejs3@npm:^0.13.0": + version: 0.13.0 + resolution: "babel-plugin-polyfill-corejs3@npm:0.13.0" dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.6.3" - core-js-compat: "npm:^3.40.0" + "@babel/helper-define-polyfill-provider": "npm:^0.6.5" + core-js-compat: "npm:^3.43.0" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/19a2978ee3462cc3b98e7d36e6537bf9fb1fb61f42fd96cb41e9313f2ac6f2c62380d94064366431eff537f342184720fe9bce73eb65fd57c5311d15e8648f62 + checksum: 10/aa36f9a09521404dd0569a4cbd5f88aa4b9abff59508749abde5d09d66c746012fb94ed1e6e2c8be3710939a2a4c6293ee3be889125d7611c93e5897d9e5babd languageName: node linkType: hard -"babel-plugin-polyfill-regenerator@npm:^0.6.1": - version: 0.6.4 - resolution: "babel-plugin-polyfill-regenerator@npm:0.6.4" +"babel-plugin-polyfill-regenerator@npm:^0.6.5": + version: 0.6.5 + resolution: "babel-plugin-polyfill-regenerator@npm:0.6.5" dependencies: - "@babel/helper-define-polyfill-provider": "npm:^0.6.4" + "@babel/helper-define-polyfill-provider": "npm:^0.6.5" peerDependencies: "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 - checksum: 10/f4d4a803834ffa72713579d696586d8cc654c0025cbd5ec775fc5d37faa00381dcb80e5b97d4b16059443352653585596d87848b5590b1d8670c235408e73fb3 + checksum: 10/ed1932fa9a31e0752fd10ebf48ab9513a654987cab1182890839523cb898559d24ae0578fdc475d9f995390420e64eeaa4b0427045b56949dace3c725bc66dbb languageName: node linkType: hard @@ -6938,7 +6952,7 @@ __metadata: languageName: node linkType: hard -"core-js-compat@npm:^3.40.0": +"core-js-compat@npm:^3.43.0": version: 3.43.0 resolution: "core-js-compat@npm:3.43.0" dependencies: @@ -9286,10 +9300,10 @@ __metadata: version: 0.0.0-use.local resolution: "home-assistant-frontend@workspace:." dependencies: - "@babel/core": "npm:7.27.7" + "@babel/core": "npm:7.28.0" "@babel/helper-define-polyfill-provider": "npm:0.6.5" - "@babel/plugin-transform-runtime": "npm:7.27.4" - "@babel/preset-env": "npm:7.27.2" + "@babel/plugin-transform-runtime": "npm:7.28.0" + "@babel/preset-env": "npm:7.28.0" "@babel/runtime": "npm:7.27.6" "@braintree/sanitize-url": "npm:7.1.1" "@bundle-stats/plugin-webpack-filter": "npm:4.21.0" From 27b36707e5e40205d208978bd6d6ea23e52a2b19 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sun, 6 Jul 2025 09:57:16 +0200 Subject: [PATCH 207/216] Automation save dialog to suggest name, description and labels (#26071) * AI Task structure * Suggest description and labels too --- src/data/ai_task.ts | 20 ++- .../dialog-automation-save.ts | 123 ++++++++++++++++-- 2 files changed, 130 insertions(+), 13 deletions(-) diff --git a/src/data/ai_task.ts b/src/data/ai_task.ts index 451fced583..c89f6607fe 100644 --- a/src/data/ai_task.ts +++ b/src/data/ai_task.ts @@ -1,14 +1,23 @@ import type { HomeAssistant } from "../types"; +import type { Selector } from "./selector"; export interface AITaskPreferences { gen_data_entity_id: string | null; } -export interface GenDataTaskResult { +export interface GenDataTaskResult { conversation_id: string; - data: string; + data: T; } +export interface AITaskStructureField { + description?: string; + required?: boolean; + selector: Selector; +} + +export type AITaskStructure = Record; + export const fetchAITaskPreferences = (hass: HomeAssistant) => hass.callWS({ type: "ai_task/preferences/get", @@ -23,15 +32,16 @@ export const saveAITaskPreferences = ( ...preferences, }); -export const generateDataAITask = async ( +export const generateDataAITask = async ( hass: HomeAssistant, task: { task_name: string; entity_id?: string; instructions: string; + structure?: AITaskStructure; } -): Promise => { - const result = await hass.callService( +): Promise> => { + const result = await hass.callService>( "ai_task", "generate_data", task, diff --git a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts index 2ba4069d0d..6500646071 100644 --- a/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts +++ b/src/panels/config/automation/automation-save-dialog/dialog-automation-save.ts @@ -30,6 +30,9 @@ import { generateDataAITask, } from "../../../../data/ai_task"; import { isComponentLoaded } from "../../../../common/config/is_component_loaded"; +import { computeStateDomain } from "../../../../common/entity/compute_state_domain"; +import { subscribeOne } from "../../../../common/util/subscribe-one"; +import { subscribeLabelRegistry } from "../../../../data/label_registry"; @customElement("ha-dialog-automation-save") class DialogAutomationSave extends LitElement implements HassDialog { @@ -75,7 +78,7 @@ class DialogAutomationSave extends LitElement implements HassDialog { this._entryUpdates.category ? "category" : "", this._entryUpdates.labels.length > 0 ? "labels" : "", this._entryUpdates.area ? "area" : "", - ]; + ].filter(Boolean); } public closeDialog() { @@ -346,17 +349,121 @@ class DialogAutomationSave extends LitElement implements HassDialog { } private async _suggest() { - const result = await generateDataAITask(this.hass, { - task_name: "frontend:automation:save", - instructions: `Suggest one name for the following Home Assistant automation. -Your answer should only contain the name, without any additional text or formatting. -The name should be relevant to the automation's purpose and should not exceed 50 characters. -The name should be short, descriptive, sentence case, and written in the language ${this.hass.language}. + const labels = await subscribeOne( + this.hass.connection, + subscribeLabelRegistry + ).then((labs) => + Object.fromEntries(labs.map((lab) => [lab.label_id, lab.name])) + ); + const automationInspiration: string[] = []; + for (const automation of Object.values(this.hass.states)) { + const entityEntry = this.hass.entities[automation.entity_id]; + if ( + computeStateDomain(automation) !== "automation" || + automation.attributes.restored || + !automation.attributes.friendly_name || + !entityEntry + ) { + continue; + } + + let inspiration = `- ${automation.attributes.friendly_name}`; + + if (entityEntry.labels.length) { + inspiration += ` (labels: ${entityEntry.labels + .map((label) => labels[label]) + .join(", ")})`; + } + + automationInspiration.push(inspiration); + } + + const result = await generateDataAITask<{ + name: string; + description: string | undefined; + labels: string[] | undefined; + }>(this.hass, { + task_name: "frontend:automation:save", + instructions: `Suggest in language "${this.hass.language}" a name, description, and labels for the following Home Assistant automation. + +The name should be relevant to the automation's purpose. +${ + automationInspiration.length + ? `The name should be in same style as existing automations. +Suggest labels if relevant to the automation's purpose. +Only suggest labels that are already used by existing automations.` + : `The name should be short, descriptive, sentence case, and written in the language ${this.hass.language}.` +} +If the automation contains 5+ steps, include a short description. + +For inspiration, here are existing automations: +${automationInspiration.join("\n")} + +The automation configuration is as follows: ${dump(this._params.config)} `, + structure: { + name: { + description: "The name of the automation", + required: true, + selector: { + text: {}, + }, + }, + description: { + description: "A short description of the automation", + required: false, + selector: { + text: {}, + }, + }, + labels: { + description: "Labels for the automation", + required: false, + selector: { + text: { + multiple: true, + }, + }, + }, + }, }); - this._newName = result.data.trim(); + this._newName = result.data.name; + if (result.data.description) { + this._newDescription = result.data.description; + if (!this._visibleOptionals.includes("description")) { + this._visibleOptionals = [...this._visibleOptionals, "description"]; + } + } + if (result.data.labels?.length) { + // We get back label names, convert them to IDs + const newLabels: Record = Object.fromEntries( + result.data.labels.map((name) => [name, undefined]) + ); + let toFind = result.data.labels.length; + for (const [labelId, labelName] of Object.entries(labels)) { + if (labelName in newLabels && newLabels[labelName] === undefined) { + newLabels[labelName] = labelId; + toFind--; + if (toFind === 0) { + break; + } + } + } + const foundLabels = Object.values(newLabels).filter( + (labelId) => labelId !== undefined + ); + if (foundLabels.length) { + this._entryUpdates = { + ...this._entryUpdates, + labels: foundLabels, + }; + if (!this._visibleOptionals.includes("labels")) { + this._visibleOptionals = [...this._visibleOptionals, "labels"]; + } + } + } } private async _save(): Promise { From 2b056c04342aa5dec6ce9a35c40b7b5dff4057c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 6 Jul 2025 09:58:06 +0200 Subject: [PATCH 208/216] Update dependency @lokalise/node-api to v14.9.1 (#26081) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9ebeed5086..459aec4853 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@babel/plugin-transform-runtime": "7.28.0", "@babel/preset-env": "7.28.0", "@bundle-stats/plugin-webpack-filter": "4.21.0", - "@lokalise/node-api": "14.9.0", + "@lokalise/node-api": "14.9.1", "@octokit/auth-oauth-device": "8.0.1", "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", diff --git a/yarn.lock b/yarn.lock index 6869a11a0b..1992292a19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2290,10 +2290,10 @@ __metadata: languageName: node linkType: hard -"@lokalise/node-api@npm:14.9.0": - version: 14.9.0 - resolution: "@lokalise/node-api@npm:14.9.0" - checksum: 10/d8960d60d563719e960acf63d4179246ee581e35f25c56b82a3c877f9dbd3260c6c27926a55a128c4a8c8979851b3fa3b9041964298cb8a09b5d2645152105be +"@lokalise/node-api@npm:14.9.1": + version: 14.9.1 + resolution: "@lokalise/node-api@npm:14.9.1" + checksum: 10/fe0bd669f52ed5b52535547724b940445e2a1356107ea531b291cb0bee9d799ebc3698b1a08f4736cc7ca3a3aeb7991f3fe0f11ae4b8592580d43796b24c6401 languageName: node linkType: hard @@ -9336,7 +9336,7 @@ __metadata: "@lit-labs/virtualizer": "npm:2.1.0" "@lit/context": "npm:1.1.5" "@lit/reactive-element": "npm:2.1.0" - "@lokalise/node-api": "npm:14.9.0" + "@lokalise/node-api": "npm:14.9.1" "@material/chips": "npm:=14.0.0-canary.53b3cad2f.0" "@material/data-table": "npm:=14.0.0-canary.53b3cad2f.0" "@material/mwc-base": "npm:0.27.0" From 5ffe37407a380f8d6bba4dab236d2eed34879716 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 6 Jul 2025 09:58:49 +0200 Subject: [PATCH 209/216] Update dependency hls.js to v1.6.6 (#26085) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 459aec4853..a81235e81f 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "fuse.js": "7.1.0", "google-timezones-json": "1.2.0", "gulp-zopfli-green": "6.0.2", - "hls.js": "1.6.5", + "hls.js": "1.6.6", "home-assistant-js-websocket": "9.5.0", "idb-keyval": "6.2.2", "intl-messageformat": "10.7.16", diff --git a/yarn.lock b/yarn.lock index 1992292a19..4b09ef2db6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9289,10 +9289,10 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:1.6.5": - version: 1.6.5 - resolution: "hls.js@npm:1.6.5" - checksum: 10/17a03ba52ded64cfbd917ed1359f0667c56d1a98f58c5690ee5a181d95d8fc10e86f81473bb61efa75af51e8d3107a4c7cf1dd4e1e1723c7d3a758824bb7dc53 +"hls.js@npm:1.6.6": + version: 1.6.6 + resolution: "hls.js@npm:1.6.6" + checksum: 10/427988b169a0811a2863d818fd139cfaf457108b69da6c03819a9e2bff984e7472977b1bc83303f7cd3cd34096a03f1004518c4e8a1697dbe0c0a551082df338 languageName: node linkType: hard @@ -9435,7 +9435,7 @@ __metadata: gulp-json-transform: "npm:0.5.0" gulp-rename: "npm:2.1.0" gulp-zopfli-green: "npm:6.0.2" - hls.js: "npm:1.6.5" + hls.js: "npm:1.6.6" home-assistant-js-websocket: "npm:9.5.0" html-minifier-terser: "npm:7.2.0" husky: "npm:9.1.7" From 5c346798c84152061d92a5c6bbc23050b763a6e6 Mon Sep 17 00:00:00 2001 From: Yosi Levy <37745463+yosilevy@users.noreply.github.com> Date: Sun, 6 Jul 2025 11:04:09 +0300 Subject: [PATCH 210/216] RTL fixes for 7-25 (#26074) --- src/panels/config/dashboard/dashboard-card.ts | 2 +- .../config/integrations/ha-config-integration-page.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/panels/config/dashboard/dashboard-card.ts b/src/panels/config/dashboard/dashboard-card.ts index b737152442..663500dc33 100644 --- a/src/panels/config/dashboard/dashboard-card.ts +++ b/src/panels/config/dashboard/dashboard-card.ts @@ -58,7 +58,7 @@ export class DashboardCard extends LitElement { .card-header { padding: 12px; display: block; - text-align: left; + text-align: var(--float-start); gap: 8px; } .preview { diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 744b3fd181..74f5be55d6 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -907,7 +907,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { justify-content: space-between; } .title h1 { - font-family: Roboto; + font-family: var(--ha-font-family-body); font-size: 32px; font-weight: 700; line-height: 40px; @@ -938,6 +938,8 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { display: flex; justify-content: center; margin-right: 16px; + margin-inline-end: 16px; + margin-inline-start: initial; padding: 0 8px; } .logo-container img { @@ -971,7 +973,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { margin-inline-start: 16px; margin-top: 6px; margin-bottom: 6px; - font-family: Roboto; + font-family: var(--ha-font-family-body); font-size: 14px; font-weight: 500; line-height: 20px; From d6b66a7145716da4951e8327720c58d66ad144dc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 6 Jul 2025 08:07:11 +0000 Subject: [PATCH 211/216] Update dependency @rsdoctor/rspack-plugin to v1.1.7 (#26087) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 90 ++++++++++++++++++++++++++-------------------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index a81235e81f..8f0b5b8a74 100644 --- a/package.json +++ b/package.json @@ -158,7 +158,7 @@ "@octokit/auth-oauth-device": "8.0.1", "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", - "@rsdoctor/rspack-plugin": "1.1.6", + "@rsdoctor/rspack-plugin": "1.1.7", "@rspack/cli": "1.4.2", "@rspack/core": "1.4.2", "@types/babel__plugin-transform-runtime": "7.9.5", diff --git a/yarn.lock b/yarn.lock index 4b09ef2db6..97752b5550 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3818,22 +3818,22 @@ __metadata: languageName: node linkType: hard -"@rsdoctor/client@npm:1.1.6": - version: 1.1.6 - resolution: "@rsdoctor/client@npm:1.1.6" - checksum: 10/979c05ee26cf9082fd893f5794f3339a9032b7a1495fef0ab3aa83272ad2fff615107e89a2ed54b791a113620ecb5533b406c91c80ae215fa9bd32e26a9516ed +"@rsdoctor/client@npm:1.1.7": + version: 1.1.7 + resolution: "@rsdoctor/client@npm:1.1.7" + checksum: 10/4d17a357414f50b8ecb52e22530b6e657106c70b9c94d41f90f2ed2c833fddede80b3d23fb1aba47584719fa704391413124010d3a48ff065a8666dd07028ec8 languageName: node linkType: hard -"@rsdoctor/core@npm:1.1.6": - version: 1.1.6 - resolution: "@rsdoctor/core@npm:1.1.6" +"@rsdoctor/core@npm:1.1.7": + version: 1.1.7 + resolution: "@rsdoctor/core@npm:1.1.7" dependencies: "@rsbuild/plugin-check-syntax": "npm:1.3.0" - "@rsdoctor/graph": "npm:1.1.6" - "@rsdoctor/sdk": "npm:1.1.6" - "@rsdoctor/types": "npm:1.1.6" - "@rsdoctor/utils": "npm:1.1.6" + "@rsdoctor/graph": "npm:1.1.7" + "@rsdoctor/sdk": "npm:1.1.7" + "@rsdoctor/types": "npm:1.1.7" + "@rsdoctor/utils": "npm:1.1.7" axios: "npm:^1.10.0" browserslist-load-config: "npm:^1.0.0" enhanced-resolve: "npm:5.12.0" @@ -3844,50 +3844,50 @@ __metadata: semver: "npm:^7.7.2" source-map: "npm:^0.7.4" webpack-bundle-analyzer: "npm:^4.10.2" - checksum: 10/04643bf79cfe44085baa506f7532c57caf468ef6f4fa58a9a93ae297f03b550092a0590b5ad93bce3bab8cedfbbb3ae8f3d151d67440c17d2cf1a350df872783 + checksum: 10/30a0adf465501cdaab1b8422529d21224935f61fb773a52075be4ba9d8ca684140bc1220e2ef1f77fbd75944645a564bd7eaba930dedb8c49f267fe0dcd99a73 languageName: node linkType: hard -"@rsdoctor/graph@npm:1.1.6": - version: 1.1.6 - resolution: "@rsdoctor/graph@npm:1.1.6" +"@rsdoctor/graph@npm:1.1.7": + version: 1.1.7 + resolution: "@rsdoctor/graph@npm:1.1.7" dependencies: - "@rsdoctor/types": "npm:1.1.6" - "@rsdoctor/utils": "npm:1.1.6" + "@rsdoctor/types": "npm:1.1.7" + "@rsdoctor/utils": "npm:1.1.7" lodash.unionby: "npm:^4.8.0" socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" - checksum: 10/ff3eb5191e39ba7c53fcab11423a23bd7c74c9342a8aa23e3134d7f451fd31ac8d6bb6af9b18728e16dfa5d3f46933cf1d56c3828a1b8c4664eb2353be7f9c71 + checksum: 10/4314beb5119c7082df8b046c23fa27e4bcd80e3a504188e8a9e0d84e58713fae32e720b2cc4639f04ef53e891a323871e9378d37548769961a87934eff79825d languageName: node linkType: hard -"@rsdoctor/rspack-plugin@npm:1.1.6": - version: 1.1.6 - resolution: "@rsdoctor/rspack-plugin@npm:1.1.6" +"@rsdoctor/rspack-plugin@npm:1.1.7": + version: 1.1.7 + resolution: "@rsdoctor/rspack-plugin@npm:1.1.7" dependencies: - "@rsdoctor/core": "npm:1.1.6" - "@rsdoctor/graph": "npm:1.1.6" - "@rsdoctor/sdk": "npm:1.1.6" - "@rsdoctor/types": "npm:1.1.6" - "@rsdoctor/utils": "npm:1.1.6" + "@rsdoctor/core": "npm:1.1.7" + "@rsdoctor/graph": "npm:1.1.7" + "@rsdoctor/sdk": "npm:1.1.7" + "@rsdoctor/types": "npm:1.1.7" + "@rsdoctor/utils": "npm:1.1.7" lodash: "npm:^4.17.21" peerDependencies: "@rspack/core": "*" peerDependenciesMeta: "@rspack/core": optional: true - checksum: 10/c3fee7db4bd25b63638d43b272c3df68c44b96f3d31b33e4501c8ea47860d9e7506cee2a493090e99c5beca1fced218fb390ddcc6092da1e47440fa7bc28be72 + checksum: 10/c2a4dfcf5bd18b59e1acadac62f8650847a634dfe469a5b15c217d94856f6a66170b743cc16af027f0c8096f05914423dac72fd2cfd453d95e1750918506d2b7 languageName: node linkType: hard -"@rsdoctor/sdk@npm:1.1.6": - version: 1.1.6 - resolution: "@rsdoctor/sdk@npm:1.1.6" +"@rsdoctor/sdk@npm:1.1.7": + version: 1.1.7 + resolution: "@rsdoctor/sdk@npm:1.1.7" dependencies: - "@rsdoctor/client": "npm:1.1.6" - "@rsdoctor/graph": "npm:1.1.6" - "@rsdoctor/types": "npm:1.1.6" - "@rsdoctor/utils": "npm:1.1.6" + "@rsdoctor/client": "npm:1.1.7" + "@rsdoctor/graph": "npm:1.1.7" + "@rsdoctor/types": "npm:1.1.7" + "@rsdoctor/utils": "npm:1.1.7" "@types/fs-extra": "npm:^11.0.4" body-parser: "npm:1.20.3" cors: "npm:2.8.5" @@ -3900,13 +3900,13 @@ __metadata: socket.io: "npm:4.8.1" source-map: "npm:^0.7.4" tapable: "npm:2.2.2" - checksum: 10/7e38f4a1d6034d6a4d4ac0173757e967284290e256b74a2d3cb92a360169bea85f0f0f15c2f21a328f5460ab355ba5de213deada70794fd4f50ade096df481ad + checksum: 10/e2b24eb7ac5aaf2872a4f4d3483be65ca4e9d17311a1fc1b4648c05cb697b232a2be1be3a3f5dc2afbcfb2d92f4373fa2b5d80d926a49bb1cb7238a8e1e5c41f languageName: node linkType: hard -"@rsdoctor/types@npm:1.1.6": - version: 1.1.6 - resolution: "@rsdoctor/types@npm:1.1.6" +"@rsdoctor/types@npm:1.1.7": + version: 1.1.7 + resolution: "@rsdoctor/types@npm:1.1.7" dependencies: "@types/connect": "npm:3.4.38" "@types/estree": "npm:1.0.5" @@ -3920,16 +3920,16 @@ __metadata: optional: true webpack: optional: true - checksum: 10/cca6a83b22126bfc99c4c8428ee2a85dac7dc41f98307ee14019b2418e11197c0a79447d32f521e9d045aec9ca2829740935a865bb292db9b7b925da75bd0ea2 + checksum: 10/fd5aec14068746fb25dda4e93c6a804b01970dfe0c5d4a7f30dc6097923dc19b105fd5e899b279c066cc027666fa814c401bbdb1a3988a31dea8686830856050 languageName: node linkType: hard -"@rsdoctor/utils@npm:1.1.6": - version: 1.1.6 - resolution: "@rsdoctor/utils@npm:1.1.6" +"@rsdoctor/utils@npm:1.1.7": + version: 1.1.7 + resolution: "@rsdoctor/utils@npm:1.1.7" dependencies: "@babel/code-frame": "npm:7.26.2" - "@rsdoctor/types": "npm:1.1.6" + "@rsdoctor/types": "npm:1.1.7" "@types/estree": "npm:1.0.5" acorn: "npm:^8.10.0" acorn-import-attributes: "npm:^1.9.5" @@ -3945,7 +3945,7 @@ __metadata: picocolors: "npm:^1.1.1" rslog: "npm:^1.2.8" strip-ansi: "npm:^6.0.1" - checksum: 10/6c8e9a3ba5b8891892dfe65508b78dfce8b4fb7e9a2d66051b51739910cf76bb0a0864966ac20dd1ddb2dc4b3676c0201171ae38dcefdfda7b38bf14e8c930da + checksum: 10/70b1fbf149f79d574c889f39e01303898ced2215d6a964d0359c7286e0609f6b9a057a5b54913b76a27312ca0469995bd05d2074fbc82879c59331cef1143ee2 languageName: node linkType: hard @@ -9367,7 +9367,7 @@ __metadata: "@octokit/plugin-retry": "npm:8.0.1" "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" - "@rsdoctor/rspack-plugin": "npm:1.1.6" + "@rsdoctor/rspack-plugin": "npm:1.1.7" "@rspack/cli": "npm:1.4.2" "@rspack/core": "npm:1.4.2" "@shoelace-style/shoelace": "npm:2.20.1" From e75331e1596e5934aa70afb32df47136093da61f Mon Sep 17 00:00:00 2001 From: Ezra Freedman <38084742+ezra-freedman@users.noreply.github.com> Date: Sun, 6 Jul 2025 04:12:59 -0400 Subject: [PATCH 212/216] Weather card smallest width is not set correctly (#26082) set result.width, not result.height --- src/panels/lovelace/cards/hui-weather-forecast-card.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts index 7c2759102d..aa5d4ff488 100644 --- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts +++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts @@ -82,7 +82,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { const width = entries[0]?.contentRect.width; if (width < 245) { - result.height = "very-very-narrow"; + result.width = "very-very-narrow"; } else if (width < 300) { result.width = "very-narrow"; } else if (width < 375) { @@ -93,7 +93,6 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { if (height < 235) { result.height = "short"; } - return result; }, }); From 3bb930b906e361d804590d3ddd5ea4e17cd98fc0 Mon Sep 17 00:00:00 2001 From: steinmn <46349253+steinmn@users.noreply.github.com> Date: Mon, 7 Jul 2025 07:29:25 +0200 Subject: [PATCH 213/216] Fix flickering Edit sidebar dialog by locking content padding (#26084) Fix flickering Edit sidebar dialog --- src/dialogs/sidebar/dialog-edit-sidebar.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dialogs/sidebar/dialog-edit-sidebar.ts b/src/dialogs/sidebar/dialog-edit-sidebar.ts index fed10ad593..692443a40e 100644 --- a/src/dialogs/sidebar/dialog-edit-sidebar.ts +++ b/src/dialogs/sidebar/dialog-edit-sidebar.ts @@ -208,6 +208,7 @@ class DialogEditSidebar extends LitElement { ha-md-dialog { min-width: 600px; max-height: 90%; + --dialog-content-padding: 8px 24px; } @media all and (max-width: 600px), all and (max-height: 500px) { From c5f57f436c2f67c0de6d318edc66a007106f643e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 10:17:53 +0300 Subject: [PATCH 214/216] Update rspack monorepo to v1.4.3 (#26093) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 110 +++++++++++++++++++++++++-------------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 8f0b5b8a74..8e96f0682e 100644 --- a/package.json +++ b/package.json @@ -159,8 +159,8 @@ "@octokit/plugin-retry": "8.0.1", "@octokit/rest": "22.0.0", "@rsdoctor/rspack-plugin": "1.1.7", - "@rspack/cli": "1.4.2", - "@rspack/core": "1.4.2", + "@rspack/cli": "1.4.3", + "@rspack/core": "1.4.3", "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.22", "@types/chromecast-caf-sender": "1.0.11", diff --git a/yarn.lock b/yarn.lock index 97752b5550..1f87fba68c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3949,92 +3949,92 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-darwin-arm64@npm:1.4.2" +"@rspack/binding-darwin-arm64@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-darwin-arm64@npm:1.4.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-darwin-x64@npm:1.4.2" +"@rspack/binding-darwin-x64@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-darwin-x64@npm:1.4.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.4.2" +"@rspack/binding-linux-arm64-gnu@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.4.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.4.2" +"@rspack/binding-linux-arm64-musl@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.4.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.4.2" +"@rspack/binding-linux-x64-gnu@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.4.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-linux-x64-musl@npm:1.4.2" +"@rspack/binding-linux-x64-musl@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-linux-x64-musl@npm:1.4.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-wasm32-wasi@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-wasm32-wasi@npm:1.4.2" +"@rspack/binding-wasm32-wasi@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-wasm32-wasi@npm:1.4.3" dependencies: "@napi-rs/wasm-runtime": "npm:^0.2.11" conditions: cpu=wasm32 languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.4.2" +"@rspack/binding-win32-arm64-msvc@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.4.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.4.2" +"@rspack/binding-win32-ia32-msvc@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.4.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.4.2" +"@rspack/binding-win32-x64-msvc@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.4.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/binding@npm:1.4.2" +"@rspack/binding@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/binding@npm:1.4.3" dependencies: - "@rspack/binding-darwin-arm64": "npm:1.4.2" - "@rspack/binding-darwin-x64": "npm:1.4.2" - "@rspack/binding-linux-arm64-gnu": "npm:1.4.2" - "@rspack/binding-linux-arm64-musl": "npm:1.4.2" - "@rspack/binding-linux-x64-gnu": "npm:1.4.2" - "@rspack/binding-linux-x64-musl": "npm:1.4.2" - "@rspack/binding-wasm32-wasi": "npm:1.4.2" - "@rspack/binding-win32-arm64-msvc": "npm:1.4.2" - "@rspack/binding-win32-ia32-msvc": "npm:1.4.2" - "@rspack/binding-win32-x64-msvc": "npm:1.4.2" + "@rspack/binding-darwin-arm64": "npm:1.4.3" + "@rspack/binding-darwin-x64": "npm:1.4.3" + "@rspack/binding-linux-arm64-gnu": "npm:1.4.3" + "@rspack/binding-linux-arm64-musl": "npm:1.4.3" + "@rspack/binding-linux-x64-gnu": "npm:1.4.3" + "@rspack/binding-linux-x64-musl": "npm:1.4.3" + "@rspack/binding-wasm32-wasi": "npm:1.4.3" + "@rspack/binding-win32-arm64-msvc": "npm:1.4.3" + "@rspack/binding-win32-ia32-msvc": "npm:1.4.3" + "@rspack/binding-win32-x64-msvc": "npm:1.4.3" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -4056,13 +4056,13 @@ __metadata: optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/b5bd023fadc6a7bf03c7c34e318842fcc0101be9fed3d94b031d957995ec71af33239b7709ee4f1e7f7725eb13adb6b4e15d66166245173a8c7db5080db4f72f + checksum: 10/cfcbc90a82093ed1258788e407db8e83094c798d24ca01e17e8c8eee8df56d4e921ec17b2acd133c571c3a388f03d5cb9d9cf33318d0f48a4a363ed9796e2f33 languageName: node linkType: hard -"@rspack/cli@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/cli@npm:1.4.2" +"@rspack/cli@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/cli@npm:1.4.3" dependencies: "@discoveryjs/json-ext": "npm:^0.5.7" "@rspack/dev-server": "npm:~1.1.3" @@ -4076,23 +4076,23 @@ __metadata: "@rspack/core": ^1.0.0-alpha || ^1.x bin: rspack: bin/rspack.js - checksum: 10/9ffaaf3cf2a07cbd67511291bb5f64e9aca838a974008d5d5c1c3ac78ca58f4ae72ca82a96f0b432628754913aed56b2f6c3a2f5b450f051522f33f6422e4abd + checksum: 10/9b431bd4a8a994f5bf9f98cfd2f6b2e74f67b54d6b26485d8a73e35b8354bebb3e46b6b24265acababef9ae3a6f9e345d90a16ae2554c4476d87cfb3cea0238b languageName: node linkType: hard -"@rspack/core@npm:1.4.2": - version: 1.4.2 - resolution: "@rspack/core@npm:1.4.2" +"@rspack/core@npm:1.4.3": + version: 1.4.3 + resolution: "@rspack/core@npm:1.4.3" dependencies: "@module-federation/runtime-tools": "npm:0.15.0" - "@rspack/binding": "npm:1.4.2" + "@rspack/binding": "npm:1.4.3" "@rspack/lite-tapable": "npm:1.0.1" peerDependencies: "@swc/helpers": ">=0.5.1" peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/c47addb078c200bf32fb76fe07dd90c3ad9aab245291b6a5d695868db238b9a3ef36526028ac67081e1ae46547afff6b94ddbb7bd5899948f008c94bc938df45 + checksum: 10/cbf20531a992d227cbfc3ce2c110d08cf093ad9d8dc5f516611a12755c507271da600cf9ed89f62e91fa15c9cf2ac7417b3bb3e46e80d67f0e66c9ce37ace9f0 languageName: node linkType: hard @@ -9368,8 +9368,8 @@ __metadata: "@octokit/rest": "npm:22.0.0" "@replit/codemirror-indentation-markers": "npm:6.5.3" "@rsdoctor/rspack-plugin": "npm:1.1.7" - "@rspack/cli": "npm:1.4.2" - "@rspack/core": "npm:1.4.2" + "@rspack/cli": "npm:1.4.3" + "@rspack/core": "npm:1.4.3" "@shoelace-style/shoelace": "npm:2.20.1" "@swc/helpers": "npm:0.5.17" "@thomasloven/round-slider": "npm:0.6.0" From b60f2e320143d1d4891d88fa249743e006b98284 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 7 Jul 2025 11:11:35 +0200 Subject: [PATCH 215/216] Add extra margin AI Task pref (#26096) Add extra margin AI Task --- src/panels/config/core/ha-config-section-general.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/config/core/ha-config-section-general.ts b/src/panels/config/core/ha-config-section-general.ts index 7fc6aa15e4..1d014dfd2b 100644 --- a/src/panels/config/core/ha-config-section-general.ts +++ b/src/panels/config/core/ha-config-section-general.ts @@ -394,7 +394,8 @@ class HaConfigSectionGeneral extends LitElement { flex-direction: column; display: flex; } - ha-card { + ha-card, + ai-task-pref { margin-bottom: 24px; } .card-content { From 2e8203f666f6363e5d49bcf385eea1e31a46bd9b Mon Sep 17 00:00:00 2001 From: Kevin Lakotko Date: Mon, 7 Jul 2025 12:23:27 -0400 Subject: [PATCH 216/216] Sort groups if same as sort column (#26010) * fix(grouping): if sorted by column sort group * chore: use props to group for memoization --- src/components/data-table/ha-data-table.ts | 43 +++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index 0369758b29..c625e9782d 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -507,7 +507,9 @@ export class HaDataTable extends LitElement { this.hasFab, this.groupColumn, this.groupOrder, - this._collapsedGroups + this._collapsedGroups, + this.sortColumn, + this.sortDirection )} .keyFunction=${this._keyFunction} .renderItem=${renderRow} @@ -702,22 +704,37 @@ export class HaDataTable extends LitElement { hasFab: boolean, groupColumn: string | undefined, groupOrder: string[] | undefined, - collapsedGroups: string[] + collapsedGroups: string[], + sortColumn: string | undefined, + sortDirection: SortingDirection ) => { if (appendRow || hasFab || groupColumn) { let items = [...data]; if (groupColumn) { + const isGroupSortColumn = sortColumn === groupColumn; const grouped = groupBy(items, (item) => item[groupColumn]); if (grouped.undefined) { // make sure ungrouped items are at the bottom grouped[UNDEFINED_GROUP_KEY] = grouped.undefined; delete grouped.undefined; } - const sorted: Record = Object.keys( + const sortedEntries: [string, DataTableRowData[]][] = Object.keys( grouped ) .sort((a, b) => { + if (!groupOrder && isGroupSortColumn) { + const comparison = stringCompare( + a, + b, + this.hass.locale.language + ); + if (sortDirection === "asc") { + return comparison; + } + return comparison * -1; + } + const orderA = groupOrder?.indexOf(a) ?? -1; const orderB = groupOrder?.indexOf(b) ?? -1; if (orderA !== orderB) { @@ -735,12 +752,18 @@ export class HaDataTable extends LitElement { this.hass.locale.language ); }) - .reduce((obj, key) => { - obj[key] = grouped[key]; - return obj; - }, {}); + .reduce( + (entries, key) => { + const entry: [string, DataTableRowData[]] = [key, grouped[key]]; + + entries.push(entry); + return entries; + }, + [] as [string, DataTableRowData[]][] + ); + const groupedItems: DataTableRowData[] = []; - Object.entries(sorted).forEach(([groupName, rows]) => { + sortedEntries.forEach(([groupName, rows]) => { const collapsed = collapsedGroups.includes(groupName); groupedItems.push({ append: true, @@ -836,7 +859,9 @@ export class HaDataTable extends LitElement { this.hasFab, this.groupColumn, this.groupOrder, - this._collapsedGroups + this._collapsedGroups, + this.sortColumn, + this.sortDirection ); if (