From 2105db9104a8f83a2b150cd1ed4654d7e20d2683 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 25 Dec 2024 01:57:05 +0100 Subject: [PATCH 01/53] change default of backup actions card feature to no backup (#23444) --- .../lovelace/card-features/hui-update-actions-card-feature.ts | 2 +- .../config-elements/hui-update-actions-card-feature-editor.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/card-features/hui-update-actions-card-feature.ts b/src/panels/lovelace/card-features/hui-update-actions-card-feature.ts index aa0ec42d3d..1956df515a 100644 --- a/src/panels/lovelace/card-features/hui-update-actions-card-feature.ts +++ b/src/panels/lovelace/card-features/hui-update-actions-card-feature.ts @@ -16,7 +16,7 @@ import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types"; import { cardFeatureStyles } from "./common/card-feature-styles"; import type { UpdateActionsCardFeatureConfig } from "./types"; -export const DEFAULT_UPDATE_BACKUP_OPTION = "ask"; +export const DEFAULT_UPDATE_BACKUP_OPTION = "no"; export const supportsUpdateActionsCardFeature = (stateObj: HassEntity) => { const domain = computeDomain(stateObj.entity_id); diff --git a/src/panels/lovelace/editor/config-elements/hui-update-actions-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-update-actions-card-feature-editor.ts index 201fd343bb..9497c6ae90 100644 --- a/src/panels/lovelace/editor/config-elements/hui-update-actions-card-feature-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-update-actions-card-feature-editor.ts @@ -38,7 +38,7 @@ export class HuiUpdateActionsCardFeatureEditor disabled: !supportsBackup, selector: { select: { - default: "yes", + default: "no", mode: "dropdown", options: ["ask", "yes", "no"].map((option) => ({ value: option, From 86133a0696a8c0b158108d858ef3b325e950d7a5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 24 Dec 2024 13:39:33 -0500 Subject: [PATCH 02/53] Fix typo in backups overview (#23446) --- .../backup/components/overview/ha-backup-overview-summary.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts index 1d63ac875e..c0457774bd 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts @@ -95,7 +95,7 @@ class HaBackupOverviewBackups extends LitElement { const now = new Date(); const lastBackupDescription = lastSuccessfulBackup - ? `Last successful backup ${relativeTime(lastSuccessfulBackupDate, this.hass.locale, now, true)} and stored to ${lastSuccessfulBackup.agent_ids?.length} locations.` + ? `Last successful backup ${relativeTime(lastSuccessfulBackupDate, this.hass.locale, now, true)} and stored in ${lastSuccessfulBackup.agent_ids?.length} locations.` : "You have no successful backups."; if (lastAttempt && lastAttempt > lastSuccessfulBackupDate) { From 205dd3f968c8f03f06e74c601b5998573219c8bb Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Wed, 25 Dec 2024 11:56:26 +0100 Subject: [PATCH 03/53] Fix chip spacing in automation/script save dialog (#23451) --- .../automation-rename-dialog/dialog-automation-rename.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/panels/config/automation/automation-rename-dialog/dialog-automation-rename.ts b/src/panels/config/automation/automation-rename-dialog/dialog-automation-rename.ts index df0b7aa1d0..f9a829d618 100644 --- a/src/panels/config/automation/automation-rename-dialog/dialog-automation-rename.ts +++ b/src/panels/config/automation/automation-rename-dialog/dialog-automation-rename.ts @@ -328,8 +328,7 @@ class DialogAutomationRename extends LitElement implements HassDialog { ha-icon-picker, ha-category-picker, ha-labels-picker, - ha-area-picker, - ha-chip-set { + ha-area-picker { display: block; } ha-icon-picker, From 44e26c925bef0ab0c771953a24220fb58ed29442 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 25 Dec 2024 10:26:57 -0800 Subject: [PATCH 04/53] Fix dialog-person-detail tracker selection (#23454) --- src/panels/config/person/dialog-person-detail.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/person/dialog-person-detail.ts b/src/panels/config/person/dialog-person-detail.ts index 90dc3eb8dd..a8c9d2638f 100644 --- a/src/panels/config/person/dialog-person-detail.ts +++ b/src/panels/config/person/dialog-person-detail.ts @@ -180,7 +180,7 @@ class DialogPersonDetail extends LitElement implements HassDialog { ${this._renderUserFields()} - ${!this._deviceTrackersAvailable(this.hass) + ${this._deviceTrackersAvailable(this.hass) ? html`

${this.hass.localize( From 5c7fe04562e9610ab545a020df1ffe9fabdc2371 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Thu, 26 Dec 2024 15:18:54 +0100 Subject: [PATCH 05/53] Fix header of config entry system options dialog (#23455) Fix config entry system options dialog header --- .../dialog-config-entry-system-options.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts b/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts index d6334169e5..85f30279d3 100644 --- a/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts +++ b/src/dialogs/config-entry-system-options/dialog-config-entry-system-options.ts @@ -3,7 +3,7 @@ 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-dialog"; +import { createCloseHeading } from "../../components/ha-dialog"; import "../../components/ha-formfield"; import "../../components/ha-switch"; import type { HaSwitch } from "../../components/ha-switch"; @@ -52,14 +52,14 @@ class DialogConfigEntrySystemOptions extends LitElement { ${this._error ? html`

${this._error}
` : ""} From d3b40141822ff92a2a35926b5aed6cd422c0a463 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Sat, 28 Dec 2024 09:00:28 -0800 Subject: [PATCH 06/53] Fix backups fab spacer (#23490) --- src/panels/config/backup/ha-config-backup-backups.ts | 2 +- .../config/lovelace/resources/ha-config-lovelace-resources.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/config/backup/ha-config-backup-backups.ts b/src/panels/config/backup/ha-config-backup-backups.ts index f6063ba21c..a0675e70b8 100644 --- a/src/panels/config/backup/ha-config-backup-backups.ts +++ b/src/panels/config/backup/ha-config-backup-backups.ts @@ -301,7 +301,7 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) { return html` Date: Sun, 29 Dec 2024 18:20:14 +0000 Subject: [PATCH 07/53] Bumped version to 20241229.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index acf2d514e8..a46d758acb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241224.0" +version = "20241229.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From c9082724a8c01a9ab35cb59addd63b66b7614e54 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Mon, 30 Dec 2024 11:49:08 +0100 Subject: [PATCH 08/53] View background settings: Change transparancy to opacity (#23450) --- src/data/lovelace/config/view.ts | 2 +- .../editor/view-editor/hui-view-background-editor.ts | 10 +++++----- src/panels/lovelace/views/hui-view-background.ts | 4 ++-- src/translations/en.json | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/data/lovelace/config/view.ts b/src/data/lovelace/config/view.ts index 89a96b1a54..915c5545cb 100644 --- a/src/data/lovelace/config/view.ts +++ b/src/data/lovelace/config/view.ts @@ -9,7 +9,7 @@ export interface ShowViewConfig { export interface LovelaceViewBackgroundConfig { image?: string; - transparency?: number; + opacity?: number; size?: "auto" | "cover" | "contain"; alignment?: | "top left" diff --git a/src/panels/lovelace/editor/view-editor/hui-view-background-editor.ts b/src/panels/lovelace/editor/view-editor/hui-view-background-editor.ts index ec1a102a8d..fe5624f2a7 100644 --- a/src/panels/lovelace/editor/view-editor/hui-view-background-editor.ts +++ b/src/panels/lovelace/editor/view-editor/hui-view-background-editor.ts @@ -37,7 +37,7 @@ export class HuiViewBackgroundEditor extends LitElement { type: "expandable" as const, schema: [ { - name: "transparency", + name: "opacity", selector: { number: { min: 1, max: 100, mode: "slider" }, }, @@ -117,7 +117,7 @@ export class HuiViewBackgroundEditor extends LitElement { if (!background) { background = { - transparency: 33, + opacity: 33, alignment: "center", size: "cover", repeat: "repeat", @@ -125,7 +125,7 @@ export class HuiViewBackgroundEditor extends LitElement { }; } else { background = { - transparency: 100, + opacity: 100, alignment: "center", size: "cover", repeat: "no-repeat", @@ -162,9 +162,9 @@ export class HuiViewBackgroundEditor extends LitElement { return this.hass.localize( "ui.panel.lovelace.editor.edit_view.background.image" ); - case "transparency": + case "opacity": return this.hass.localize( - "ui.panel.lovelace.editor.edit_view.background.transparency" + "ui.panel.lovelace.editor.edit_view.background.opacity" ); case "alignment": return this.hass.localize( diff --git a/src/panels/lovelace/views/hui-view-background.ts b/src/panels/lovelace/views/hui-view-background.ts index fbb5e933c9..5a065d2650 100644 --- a/src/panels/lovelace/views/hui-view-background.ts +++ b/src/panels/lovelace/views/hui-view-background.ts @@ -67,8 +67,8 @@ export class HUIViewBackground extends LitElement { background?: string | LovelaceViewBackgroundConfig ) { if (typeof background === "object" && background.image) { - if (background.transparency) { - return `${background.transparency}%`; + if (background.opacity) { + return `${background.opacity}%`; } } return null; diff --git a/src/translations/en.json b/src/translations/en.json index 0b1477cba9..36e4533c6f 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5955,7 +5955,7 @@ "bottom right": "Bottom right" } }, - "transparency": "Background transparency", + "opacity": "Background opacity", "repeat": { "name": "Background repeat", "options": { From cb0a48265a5230f805507ec57ece346298c5c7a8 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 30 Dec 2024 12:51:21 +0200 Subject: [PATCH 09/53] Fix helper dialog close and add failsafe for similar cases (#23468) --- src/common/navigate.ts | 13 ++++++++++--- src/dialogs/make-dialog-manager.ts | 1 + src/panels/config/helpers/dialog-helper-detail.ts | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/common/navigate.ts b/src/common/navigate.ts index a134cf57f1..da636bf58b 100644 --- a/src/common/navigate.ts +++ b/src/common/navigate.ts @@ -14,9 +14,16 @@ export interface NavigateOptions { data?: any; } -export const navigate = async (path: string, options?: NavigateOptions) => { +// max time to wait for dialogs to close before navigating +const DIALOG_WAIT_TIMEOUT = 500; + +export const navigate = async ( + path: string, + options?: NavigateOptions, + timestamp = Date.now() +) => { const { history } = mainWindow; - if (history.state?.dialog) { + if (history.state?.dialog && Date.now() - timestamp < DIALOG_WAIT_TIMEOUT) { const closed = await closeAllDialogs(); if (!closed) { // eslint-disable-next-line no-console @@ -26,7 +33,7 @@ export const navigate = async (path: string, options?: NavigateOptions) => { return new Promise((resolve) => { // need to wait for history state to be updated in case a dialog was closed setTimeout(() => { - navigate(path, options).then(resolve); + navigate(path, options, timestamp).then(resolve); }); }); } diff --git a/src/dialogs/make-dialog-manager.ts b/src/dialogs/make-dialog-manager.ts index 5dd52af166..6a780e0189 100644 --- a/src/dialogs/make-dialog-manager.ts +++ b/src/dialogs/make-dialog-manager.ts @@ -225,6 +225,7 @@ export const makeDialogManager = ( }; const _handleClosedFocus = async (ev: HASSDomEvent) => { + if (!LOADED[ev.detail.dialog]) return; const closedFocusTargets = LOADED[ev.detail.dialog].closedFocusTargets; delete LOADED[ev.detail.dialog].closedFocusTargets; if (!closedFocusTargets) return; diff --git a/src/panels/config/helpers/dialog-helper-detail.ts b/src/panels/config/helpers/dialog-helper-detail.ts index 7bce02c8a3..bcbcfd74a8 100644 --- a/src/panels/config/helpers/dialog-helper-detail.ts +++ b/src/panels/config/helpers/dialog-helper-detail.ts @@ -32,6 +32,7 @@ import { brandsUrl } from "../../../util/brands-url"; import type { Helper, HelperDomain } from "./const"; import { isHelperDomain } from "./const"; import type { ShowDialogHelperDetailParams } from "./show-dialog-helper-detail"; +import { fireEvent } from "../../../common/dom/fire_event"; type HelperCreators = { [domain in HelperDomain]: { @@ -129,6 +130,7 @@ export class DialogHelperDetail extends LitElement { this._error = undefined; this._domain = undefined; this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); } protected render() { From 6d8422513a4a3c77c97f409bcab4ad05d9430478 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 30 Dec 2024 12:52:52 +0200 Subject: [PATCH 10/53] Button to reset chart zoom (#23469) --- src/components/chart/ha-chart-base.ts | 32 ++++++++++++++++++++++++++- src/translations/en.json | 3 ++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 22060e0b42..c3aab89e82 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -10,11 +10,13 @@ import { css, html, nothing, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; +import { mdiRestart } from "@mdi/js"; import { fireEvent } from "../../common/dom/fire_event"; import { clamp } from "../../common/number/clamp"; import type { HomeAssistant } from "../../types"; import { debounce } from "../../common/util/debounce"; import { isMac } from "../../util/is_mac"; +import "../ha-icon-button"; export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000; @@ -300,6 +302,16 @@ export class HaChartBase extends LitElement { : this.hass.localize("ui.components.history_charts.zoom_hint")} + ${this._isZoomed && this.chartType !== "timeline" + ? html`` + : nothing} ${this._tooltip ? html`
{ const isZoomed = this.chart?.isZoomedOrPanned() ?? false; if (this._isZoomed && !isZoomed) { @@ -541,6 +554,10 @@ export class HaChartBase extends LitElement { } } + private _handleZoomReset() { + this.chart?.resetZoom(); + } + static get styles(): CSSResultGroup { return css` :host { @@ -552,6 +569,9 @@ export class HaChartBase extends LitElement { height: 0; transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); } + .chart-container { + position: relative; + } canvas { max-height: var(--chart-max-height, 400px); } @@ -670,6 +690,16 @@ export class HaChartBase extends LitElement { background: rgba(0, 0, 0, 0.3); box-shadow: 0 0 32px 32px rgba(0, 0, 0, 0.3); } + .zoom-reset { + position: absolute; + top: 16px; + right: 4px; + background: var(--card-background-color); + border-radius: 4px; + --mdc-icon-button-size: 32px; + color: var(--primary-color); + border: 1px solid var(--divider-color); + } `; } } diff --git a/src/translations/en.json b/src/translations/en.json index 36e4533c6f..cd898e6b91 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -830,7 +830,8 @@ "source_history": "Source: History", "source_stats": "Source: Long term statistics", "zoom_hint": "Use ctrl + scroll to zoom in/out", - "zoom_hint_mac": "Use ⌘ + scroll to zoom in/out" + "zoom_hint_mac": "Use ⌘ + scroll to zoom in/out", + "zoom_reset": "Reset zoom" }, "map": { "error": "Unable to load map" From b429ecc376699f70b77ba00d7215ab413f37e933 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Fri, 27 Dec 2024 14:29:02 +0100 Subject: [PATCH 11/53] Calendar trigger: Handle optional offset better (#23474) Calendar empty offset --- src/data/automation_i18n.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/data/automation_i18n.ts b/src/data/automation_i18n.ts index 616659e682..1542d1c000 100644 --- a/src/data/automation_i18n.ts +++ b/src/data/automation_i18n.ts @@ -737,18 +737,22 @@ const tryDescribeTrigger = ( ? computeStateName(hass.states[trigger.entity_id]) : trigger.entity_id; - let offsetChoice = trigger.offset.startsWith("-") ? "before" : "after"; - let offset: string | string[] = trigger.offset.startsWith("-") - ? trigger.offset.substring(1).split(":") - : trigger.offset.split(":"); - const duration = { - hours: offset.length > 0 ? +offset[0] : 0, - minutes: offset.length > 1 ? +offset[1] : 0, - seconds: offset.length > 2 ? +offset[2] : 0, - }; - offset = formatDurationLong(hass.locale, duration); - if (offset === "") { - offsetChoice = "other"; + let offsetChoice: string = "other"; + let offset: string | string[] = ""; + if (trigger.offset) { + offsetChoice = trigger.offset.startsWith("-") ? "before" : "after"; + offset = trigger.offset.startsWith("-") + ? trigger.offset.substring(1).split(":") + : trigger.offset.split(":"); + const duration = { + hours: offset.length > 0 ? +offset[0] : 0, + minutes: offset.length > 1 ? +offset[1] : 0, + seconds: offset.length > 2 ? +offset[2] : 0, + }; + offset = formatDurationLong(hass.locale, duration); + if (offset === "") { + offsetChoice = "other"; + } } return hass.localize( From f1c360c55047ecc98b88536813b478aeadad448c Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Mon, 30 Dec 2024 12:54:00 +0200 Subject: [PATCH 12/53] Add `getGridOptions` to history and statistics graph cards (#23476) --- src/panels/lovelace/cards/hui-history-graph-card.ts | 10 +++++++++- src/panels/lovelace/cards/hui-statistics-graph-card.ts | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/cards/hui-history-graph-card.ts b/src/panels/lovelace/cards/hui-history-graph-card.ts index 490180ac91..95d6004b13 100644 --- a/src/panels/lovelace/cards/hui-history-graph-card.ts +++ b/src/panels/lovelace/cards/hui-history-graph-card.ts @@ -16,7 +16,7 @@ import { getSensorNumericDeviceClasses } from "../../../data/sensor"; import type { HomeAssistant } from "../../../types"; import { hasConfigOrEntitiesChanged } from "../common/has-changed"; import { processConfigEntities } from "../common/process-config-entities"; -import type { LovelaceCard } from "../types"; +import type { LovelaceCard, LovelaceGridOptions } from "../types"; import type { HistoryGraphCardConfig } from "./types"; import { createSearchParam } from "../../../common/url/search-params"; @@ -56,6 +56,14 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { return this._config?.title ? 2 : 0 + 2 * (this._entityIds?.length || 1); } + getGridOptions(): LovelaceGridOptions { + return { + columns: 12, + min_columns: 6, + min_rows: (this._config?.entities?.length || 1) * 2, + }; + } + public setConfig(config: HistoryGraphCardConfig): void { if (!config.entities || !Array.isArray(config.entities)) { throw new Error("Entities need to be an array"); diff --git a/src/panels/lovelace/cards/hui-statistics-graph-card.ts b/src/panels/lovelace/cards/hui-statistics-graph-card.ts index aafd5b22f1..4e703dafb4 100644 --- a/src/panels/lovelace/cards/hui-statistics-graph-card.ts +++ b/src/panels/lovelace/cards/hui-statistics-graph-card.ts @@ -18,7 +18,7 @@ import type { HomeAssistant } from "../../../types"; import { findEntities } from "../common/find-entities"; import { hasConfigOrEntitiesChanged } from "../common/has-changed"; import { processConfigEntities } from "../common/process-config-entities"; -import type { LovelaceCard } from "../types"; +import type { LovelaceCard, LovelaceGridOptions } from "../types"; import type { StatisticsGraphCardConfig } from "./types"; export const DEFAULT_DAYS_TO_SHOW = 30; @@ -93,6 +93,14 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard { ); } + getGridOptions(): LovelaceGridOptions { + return { + columns: 12, + min_columns: 9, + min_rows: 4, + }; + } + public setConfig(config: StatisticsGraphCardConfig): void { if (!config.entities || !Array.isArray(config.entities)) { throw new Error("Entities need to be an array"); From 8e8fd89d56154a42dc64c09f090de456ce8ee67d Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Fri, 27 Dec 2024 15:50:55 +0200 Subject: [PATCH 13/53] Fix custom DNS saving (#23477) --- src/panels/config/network/supervisor-network.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panels/config/network/supervisor-network.ts b/src/panels/config/network/supervisor-network.ts index 50f3afbe98..6141ae5d59 100644 --- a/src/panels/config/network/supervisor-network.ts +++ b/src/panels/config/network/supervisor-network.ts @@ -525,6 +525,9 @@ export class HassioNetwork extends LitElement { IP_VERSIONS.forEach((version) => { interfaceOptions[version] = { method: this._interface![version]?.method || "auto", + nameservers: this._interface![version]?.nameservers?.filter( + (ns: string) => ns.trim() + ), }; if (this._interface![version]?.method === "static") { interfaceOptions[version] = { @@ -533,9 +536,6 @@ export class HassioNetwork extends LitElement { (address: string) => address.trim() ), gateway: this._interface![version]?.gateway, - nameservers: this._interface![version]?.nameservers?.filter( - (ns: string) => ns.trim() - ), }; } }); From c338e9cb30d3693f967022fc2057dd8ac3f2f3b8 Mon Sep 17 00:00:00 2001 From: Timothy Kist Date: Sun, 29 Dec 2024 18:36:24 +0000 Subject: [PATCH 14/53] Remove space at end of link from HAOS storage tip (#23492) --- src/components/media-player/dialog-media-manage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/media-player/dialog-media-manage.ts b/src/components/media-player/dialog-media-manage.ts index dde5bcc547..a1a57933da 100644 --- a/src/components/media-player/dialog-media-manage.ts +++ b/src/components/media-player/dialog-media-manage.ts @@ -212,8 +212,8 @@ class DialogMediaManage extends LitElement { > ${this.hass.localize( "ui.components.media-browser.file_management.tip_storage_panel" - )} - `, + )}`, } )} ` From cf1df712e426a3cb50a8ead09ff0cd0c9aaee755 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Mon, 30 Dec 2024 11:12:31 +0100 Subject: [PATCH 15/53] Fix dialog header (#23507) --- src/components/ha-dialog.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index 863431fc66..ee96bd0f52 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -102,10 +102,10 @@ export class HaDialog extends DialogBase { align-items: var(--vertical-align-dialog, center); } .mdc-dialog__title { - padding: 12px 12px 0; + padding: 24px 24px 0 24px; } - .mdc-dialog--scrollable .mdc-dialog__title { - padding: 12px; + .mdc-dialog__title:has(span) { + padding: 12px 12px 0; } .mdc-dialog__actions { padding: 12px 24px 12px 24px; From 4686808e53cca6d8fe7fb8340375a3d14535e56d Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:05:00 +0100 Subject: [PATCH 16/53] Fix manual backup disabled with all backup locations (#23511) --- src/panels/config/backup/dialogs/dialog-generate-backup.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/config/backup/dialogs/dialog-generate-backup.ts b/src/panels/config/backup/dialogs/dialog-generate-backup.ts index 95ddb89809..866aaf441d 100644 --- a/src/panels/config/backup/dialogs/dialog-generate-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-generate-backup.ts @@ -200,7 +200,8 @@ class DialogGenerateBackup extends LitElement implements HassDialog { ? html` Create backup From 700690474ccd9d24aa2c9e46f9a0c26c5fdc162e Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:21:22 +0100 Subject: [PATCH 17/53] Add script hide picker again (#23512) --- src/components/ha-service-control.ts | 2 +- src/dialogs/more-info/controls/more-info-script.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts index 4bb9449323..5dd4222b23 100644 --- a/src/components/ha-service-control.ts +++ b/src/components/ha-service-control.ts @@ -89,7 +89,7 @@ export class HaServiceControl extends LitElement { @property({ attribute: "show-advanced", type: Boolean }) public showAdvanced = false; - @property({ attribute: false, type: Boolean, reflect: true }) + @property({ attribute: "hide-picker", type: Boolean, reflect: true }) public hidePicker = false; @property({ attribute: "hide-description", type: Boolean }) diff --git a/src/dialogs/more-info/controls/more-info-script.ts b/src/dialogs/more-info/controls/more-info-script.ts index cacaeeca3d..aba8253a0e 100644 --- a/src/dialogs/more-info/controls/more-info-script.ts +++ b/src/dialogs/more-info/controls/more-info-script.ts @@ -99,6 +99,7 @@ class MoreInfoScript extends LitElement { ${this.hass.localize("ui.card.script.run_script")}
Date: Mon, 30 Dec 2024 17:06:26 +0100 Subject: [PATCH 18/53] Add fallback for devices without name (#23513) --- src/components/device/ha-device-picker.ts | 4 +++- src/dialogs/quick-bar/ha-quick-bar.ts | 4 +++- src/translations/en.json | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/device/ha-device-picker.ts b/src/components/device/ha-device-picker.ts index 724dcbbf06..4182ee106a 100644 --- a/src/components/device/ha-device-picker.ts +++ b/src/components/device/ha-device-picker.ts @@ -222,7 +222,9 @@ export class HaDevicePicker extends LitElement { return { id: device.id, - name: name, + name: + name || + this.hass.localize("ui.components.device-picker.unnamed_device"), area: device.area_id && areas[device.area_id] ? areas[device.area_id].name diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 4b8c56d7ee..8c108e1f5f 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -530,7 +530,9 @@ export class QuickBar extends LitElement { ? this.hass.areas[device.area_id] : undefined; const deviceItem = { - primaryText: computeDeviceName(device, this.hass), + primaryText: + computeDeviceName(device, this.hass) || + this.hass.localize("ui.components.device-picker.unnamed_device"), deviceId: device.id, area: area?.name, action: () => navigate(`/config/devices/device/${device.id}`), diff --git a/src/translations/en.json b/src/translations/en.json index cd898e6b91..76c2f6ace4 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -651,6 +651,7 @@ "no_devices": "You don't have any devices", "no_match": "No matching devices found", "device": "Device", + "unnamed_device": "Unnamed device", "no_area": "No area" }, "category-picker": { From d0123b2ccecac684229f18b748867a94431602b7 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 30 Dec 2024 17:43:05 +0100 Subject: [PATCH 19/53] Fix overflow of backup agents (#23514) --- .../config/backup/components/config/ha-backup-config-agents.ts | 3 --- .../config/backup/components/config/ha-backup-config-data.ts | 3 --- .../backup/components/config/ha-backup-config-schedule.ts | 3 --- 3 files changed, 9 deletions(-) diff --git a/src/panels/config/backup/components/config/ha-backup-config-agents.ts b/src/panels/config/backup/components/config/ha-backup-config-agents.ts index 6a15f55c94..de74488dcf 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-agents.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-agents.ts @@ -144,9 +144,6 @@ class HaBackupConfigAgents extends LitElement { --md-list-item-leading-space: 0; --md-list-item-trailing-space: 0; } - ha-md-list-item { - --md-item-overflow: visible; - } ha-md-list-item img { width: 48px; } diff --git a/src/panels/config/backup/components/config/ha-backup-config-data.ts b/src/panels/config/backup/components/config/ha-backup-config-data.ts index bae085447a..be462ff3e5 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-data.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-data.ts @@ -332,9 +332,6 @@ class HaBackupConfigData extends LitElement { ha-md-select { min-width: 210px; } - ha-md-list-item { - --md-item-overflow: visible; - } @media all and (max-width: 450px) { ha-md-select { min-width: 160px; diff --git a/src/panels/config/backup/components/config/ha-backup-config-schedule.ts b/src/panels/config/backup/components/config/ha-backup-config-schedule.ts index f7c9e2e50d..0a48fc9a0e 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-schedule.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-schedule.ts @@ -323,9 +323,6 @@ class HaBackupConfigSchedule extends LitElement { ha-md-select { min-width: 210px; } - ha-md-list-item { - --md-item-overflow: visible; - } @media all and (max-width: 450px) { ha-md-select { min-width: 160px; From f3705a7e1daf7fa4a441c6fdf303bbadf938181f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 30 Dec 2024 17:42:31 +0100 Subject: [PATCH 20/53] Fix copy encryption key (#23515) --- src/common/util/copy-clipboard.ts | 8 +++++--- .../config/backup/dialogs/dialog-backup-onboarding.ts | 5 ++++- .../dialogs/dialog-change-backup-encryption-key.ts | 10 ++++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/common/util/copy-clipboard.ts b/src/common/util/copy-clipboard.ts index 1708858c85..c50ad02418 100644 --- a/src/common/util/copy-clipboard.ts +++ b/src/common/util/copy-clipboard.ts @@ -1,4 +1,4 @@ -export const copyToClipboard = async (str) => { +export const copyToClipboard = async (str, rootEl?: HTMLElement) => { if (navigator.clipboard) { try { await navigator.clipboard.writeText(str); @@ -8,10 +8,12 @@ export const copyToClipboard = async (str) => { } } + const root = rootEl ?? document.body; + const el = document.createElement("textarea"); el.value = str; - document.body.appendChild(el); + root.appendChild(el); el.select(); document.execCommand("copy"); - document.body.removeChild(el); + root.removeChild(el); }; diff --git a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts index 7719ebd066..cb5b798c50 100644 --- a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts +++ b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts @@ -396,7 +396,10 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { } private async _copyKeyToClipboard() { - await copyToClipboard(this._config!.create_backup.password!); + await copyToClipboard( + this._config!.create_backup.password!, + this.renderRoot.querySelector("div")! + ); showToast(this, { message: this.hass.localize("ui.common.copied_clipboard"), }); diff --git a/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts b/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts index 1b500b34f9..3e140c60f7 100644 --- a/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts +++ b/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts @@ -206,7 +206,10 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog { } private async _copyKeyToClipboard() { - await copyToClipboard(this._newEncryptionKey); + await copyToClipboard( + this._newEncryptionKey, + this.renderRoot.querySelector("div")! + ); showToast(this, { message: this.hass.localize("ui.common.copied_clipboard"), }); @@ -216,7 +219,10 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog { if (!this._params?.currentKey) { return; } - await copyToClipboard(this._params.currentKey); + await copyToClipboard( + this._params.currentKey, + this.renderRoot.querySelector("div")! + ); showToast(this, { message: this.hass.localize("ui.common.copied_clipboard"), }); From 8b17286fb6bdb8351921c6cf4a939e0c290ba425 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Fri, 27 Dec 2024 13:44:05 +0100 Subject: [PATCH 21/53] Revert "Automation/Script editor border-radius fix (#23267)" This reverts commit e9b2a8341142543941b0f5c366a8ab354c7d79d4. --- .../config/automation/action/ha-automation-action-row.ts | 5 ++--- .../automation/condition/ha-automation-condition-row.ts | 7 ++++--- .../config/automation/trigger/ha-automation-trigger-row.ts | 7 ++++--- src/panels/config/script/ha-script-field-row.ts | 5 ++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts index ebccfa306e..bd4fa868a1 100644 --- a/src/panels/config/automation/action/ha-automation-action-row.ts +++ b/src/panels/config/automation/action/ha-automation-action-row.ts @@ -614,9 +614,6 @@ export default class HaAutomationActionRow extends LitElement { ha-icon-button { --mdc-theme-text-primary-on-background: var(--primary-text-color); } - ha-card { - overflow: hidden; - } .disabled { opacity: 0.5; pointer-events: none; @@ -649,6 +646,8 @@ export default class HaAutomationActionRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; + border-top-right-radius: var(--ha-card-border-radius); + border-top-left-radius: var(--ha-card-border-radius); } mwc-list-item[disabled] { diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts index 8df41296e8..864a24fed4 100644 --- a/src/panels/config/automation/condition/ha-automation-condition-row.ts +++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts @@ -504,9 +504,6 @@ export default class HaAutomationConditionRow extends LitElement { ha-button-menu { --mdc-theme-text-primary-on-background: var(--primary-text-color); } - ha-card { - overflow: hidden; - } .disabled { opacity: 0.5; pointer-events: none; @@ -539,6 +536,8 @@ export default class HaAutomationConditionRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; + border-top-right-radius: var(--ha-card-border-radius); + border-top-left-radius: var(--ha-card-border-radius); } ha-list-item[disabled] { --mdc-theme-text-primary-on-background: var(--disabled-text-color); @@ -560,6 +559,8 @@ export default class HaAutomationConditionRow extends LitElement { overflow: hidden; transition: max-height 0.3s; text-align: center; + border-top-right-radius: var(--ha-card-border-radius, 12px); + border-top-left-radius: var(--ha-card-border-radius, 12px); } .testing.active { max-height: 100px; 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 13efcbf7e7..b45d776ba0 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -651,9 +651,6 @@ export default class HaAutomationTriggerRow extends LitElement { ha-button-menu { --mdc-theme-text-primary-on-background: var(--primary-text-color); } - ha-card { - overflow: hidden; - } .disabled { opacity: 0.5; pointer-events: none; @@ -686,6 +683,8 @@ export default class HaAutomationTriggerRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; + border-top-right-radius: var(--ha-card-border-radius); + border-top-left-radius: var(--ha-card-border-radius); } .triggered { cursor: pointer; @@ -702,6 +701,8 @@ export default class HaAutomationTriggerRow extends LitElement { overflow: hidden; transition: max-height 0.3s; text-align: center; + border-top-right-radius: var(--ha-card-border-radius, 12px); + border-top-left-radius: var(--ha-card-border-radius, 12px); } .triggered.active { max-height: 100px; diff --git a/src/panels/config/script/ha-script-field-row.ts b/src/panels/config/script/ha-script-field-row.ts index e652b1f8cb..2542394ce9 100644 --- a/src/panels/config/script/ha-script-field-row.ts +++ b/src/panels/config/script/ha-script-field-row.ts @@ -295,9 +295,6 @@ export default class HaScriptFieldRow extends LitElement { ha-icon-button { --mdc-theme-text-primary-on-background: var(--primary-text-color); } - ha-card { - overflow: hidden; - } .disabled { opacity: 0.5; pointer-events: none; @@ -330,6 +327,8 @@ export default class HaScriptFieldRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; + border-top-right-radius: var(--ha-card-border-radius); + border-top-left-radius: var(--ha-card-border-radius); } ha-list-item[disabled] { From 713b5c7cf7b5f1987826bb34f46c0614be29dd19 Mon Sep 17 00:00:00 2001 From: Petar Petrov Date: Fri, 27 Dec 2024 13:47:10 +0100 Subject: [PATCH 22/53] Add default border-radius values to `.disabled-bar` --- .../config/automation/action/ha-automation-action-row.ts | 4 ++-- .../automation/condition/ha-automation-condition-row.ts | 4 ++-- .../config/automation/trigger/ha-automation-trigger-row.ts | 4 ++-- src/panels/config/script/ha-script-field-row.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts index bd4fa868a1..c36f1be56f 100644 --- a/src/panels/config/automation/action/ha-automation-action-row.ts +++ b/src/panels/config/automation/action/ha-automation-action-row.ts @@ -646,8 +646,8 @@ export default class HaAutomationActionRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; - border-top-right-radius: var(--ha-card-border-radius); - border-top-left-radius: var(--ha-card-border-radius); + border-top-right-radius: var(--ha-card-border-radius, 12px); + border-top-left-radius: var(--ha-card-border-radius, 12px); } mwc-list-item[disabled] { diff --git a/src/panels/config/automation/condition/ha-automation-condition-row.ts b/src/panels/config/automation/condition/ha-automation-condition-row.ts index 864a24fed4..ded7354eca 100644 --- a/src/panels/config/automation/condition/ha-automation-condition-row.ts +++ b/src/panels/config/automation/condition/ha-automation-condition-row.ts @@ -536,8 +536,8 @@ export default class HaAutomationConditionRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; - border-top-right-radius: var(--ha-card-border-radius); - border-top-left-radius: var(--ha-card-border-radius); + border-top-right-radius: var(--ha-card-border-radius, 12px); + border-top-left-radius: var(--ha-card-border-radius, 12px); } ha-list-item[disabled] { --mdc-theme-text-primary-on-background: var(--disabled-text-color); 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 b45d776ba0..43c8758e61 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger-row.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger-row.ts @@ -683,8 +683,8 @@ export default class HaAutomationTriggerRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; - border-top-right-radius: var(--ha-card-border-radius); - border-top-left-radius: var(--ha-card-border-radius); + border-top-right-radius: var(--ha-card-border-radius, 12px); + border-top-left-radius: var(--ha-card-border-radius, 12px); } .triggered { cursor: pointer; diff --git a/src/panels/config/script/ha-script-field-row.ts b/src/panels/config/script/ha-script-field-row.ts index 2542394ce9..f214216c84 100644 --- a/src/panels/config/script/ha-script-field-row.ts +++ b/src/panels/config/script/ha-script-field-row.ts @@ -327,8 +327,8 @@ export default class HaScriptFieldRow extends LitElement { .disabled-bar { background: var(--divider-color, #e0e0e0); text-align: center; - border-top-right-radius: var(--ha-card-border-radius); - border-top-left-radius: var(--ha-card-border-radius); + border-top-right-radius: var(--ha-card-border-radius, 12px); + border-top-left-radius: var(--ha-card-border-radius, 12px); } ha-list-item[disabled] { From e8af4547054115b888e1e974e7a91ce46ec96f96 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 30 Dec 2024 19:44:56 +0100 Subject: [PATCH 23/53] Bumped version to 20241230.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a46d758acb..9aab346d0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241229.0" +version = "20241230.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 220011f15fa0cdbed0e54046e1afccb7594a47b9 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 31 Dec 2024 10:49:45 -0800 Subject: [PATCH 24/53] Display an error if saving new automation times out (#23518) --- src/common/util/promise-timeout.ts | 20 +++++++++++- .../config/automation/ha-automation-editor.ts | 31 +++++++++++++++++-- src/translations/en.json | 4 +++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/common/util/promise-timeout.ts b/src/common/util/promise-timeout.ts index 43b3359026..c38acce11a 100644 --- a/src/common/util/promise-timeout.ts +++ b/src/common/util/promise-timeout.ts @@ -1,7 +1,25 @@ +class TimeoutError extends Error { + public timeout: number; + + constructor(timeout: number, ...params) { + super(...params); + + // Maintains proper stack trace for where our error was thrown (only available on V8) + if (Error.captureStackTrace) { + Error.captureStackTrace(this, TimeoutError); + } + + this.name = "TimeoutError"; + // Custom debugging information + this.timeout = timeout; + this.message = `Timed out in ${timeout} ms.`; + } +} + export const promiseTimeout = (ms: number, promise: Promise | any) => { const timeout = new Promise((_resolve, reject) => { setTimeout(() => { - reject(`Timed out in ${ms} ms.`); + reject(new TimeoutError(ms)); }, ms); }); diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 26fe6cc0f6..e6ca397584 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -27,6 +27,7 @@ import { fireEvent } from "../../../common/dom/fire_event"; import { navigate } from "../../../common/navigate"; import { computeRTL } from "../../../common/util/compute_rtl"; import { afterNextRender } from "../../../common/util/render-status"; +import { promiseTimeout } from "../../../common/util/promise-timeout"; import "../../../components/ha-button-menu"; import "../../../components/ha-fab"; import "../../../components/ha-icon"; @@ -944,8 +945,34 @@ export class HaAutomationEditor extends PreventUnsavedMixin( // wait for automation to appear in entity registry when creating a new automation if (entityRegPromise) { - const automation = await entityRegPromise; - entityId = automation.entity_id; + try { + const automation = await promiseTimeout(2000, entityRegPromise); + entityId = automation.entity_id; + } catch (e) { + if (e instanceof Error && e.name === "TimeoutError") { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.automation.editor.new_automation_setup_failed_title", + { + type: this.hass.localize( + "ui.panel.config.automation.editor.type_automation" + ), + } + ), + text: this.hass.localize( + "ui.panel.config.automation.editor.new_automation_setup_failed_text", + { + type: this.hass.localize( + "ui.panel.config.automation.editor.type_automation" + ), + } + ), + warning: true, + }); + } else { + throw e; + } + } } if (entityId) { diff --git a/src/translations/en.json b/src/translations/en.json index 76c2f6ace4..09a80f5f99 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3065,6 +3065,10 @@ "unknown_entity": "unknown entity", "edit_unknown_device": "Editor not available for unknown device", "switch_ui_yaml_error": "There are currently YAML errors in the automation, and it cannot be parsed. Switching to UI mode may cause pending changes to be lost. Press cancel to correct any errors before proceeding to prevent loss of pending changes, or continue if you are sure.", + "type_automation": "automation", + "type_script": "script", + "new_automation_setup_failed_title": "New {type} setup failed", + "new_automation_setup_failed_text": "Your new {type} has saved, but waiting for it to setup has timed out. This could be due to errors parsing your configuration.yaml, please check the configuration in developer tools. Your {type} will not be visible until this is corrected, and automations are reloaded. Changes to area, category, or labels were not saved and must be reapplied.", "triggers": { "name": "Triggers", "header": "When", From 317a2f5b211b2f7e70aa33cb7b8629227f90dede Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 31 Dec 2024 17:48:34 +0100 Subject: [PATCH 25/53] Fix password incorrect check when restoring backup (#23525) --- src/panels/config/backup/dialogs/dialog-restore-backup.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/panels/config/backup/dialogs/dialog-restore-backup.ts b/src/panels/config/backup/dialogs/dialog-restore-backup.ts index 18a4b68a08..bcc6ecc7bf 100644 --- a/src/panels/config/backup/dialogs/dialog-restore-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-restore-backup.ts @@ -194,8 +194,9 @@ class DialogRestoreBackup extends LitElement implements HassDialog { this._userPassword || this._backupEncryptionKey ); } catch (e: any) { - this._unsubscribe(); + await this._unsubscribe(); if (e.code === "password_incorrect") { + this._error = undefined; this._step = "encryption"; } else { this._error = e.message; @@ -229,9 +230,11 @@ class DialogRestoreBackup extends LitElement implements HassDialog { private _unsubscribe() { window.removeEventListener("connection-status", this._connectionStatus); if (this._unsub) { - this._unsub.then((unsub) => unsub()); + const prom = this._unsub.then((unsub) => unsub()); this._unsub = undefined; + return prom; } + return undefined; } private _restoreState() { From c697843c3441d8d1ac479dfb2e6e42f12a8d7863 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 24 Dec 2024 17:31:02 +0100 Subject: [PATCH 26/53] Update ha-backup-overview-summary.ts --- .../backup/components/overview/ha-backup-overview-summary.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts index c0457774bd..198797e6a6 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts @@ -99,7 +99,7 @@ class HaBackupOverviewBackups extends LitElement { : "You have no successful backups."; if (lastAttempt && lastAttempt > lastSuccessfulBackupDate) { - const lastAttemptDescription = `The last automatic backup trigged ${relativeTime(lastAttempt, this.hass.locale, now, true)} wasn't successful.`; + const lastAttemptDescription = `The last automatic backup triggered ${relativeTime(lastAttempt, this.hass.locale, now, true)} wasn't successful.`; return html` Date: Tue, 31 Dec 2024 18:14:16 +0100 Subject: [PATCH 27/53] =?UTF-8?q?Use=20last=20completed=20automatic=20back?= =?UTF-8?q?up=20time=20instead=20of=20last=20available=20ba=E2=80=A6=20(#2?= =?UTF-8?q?3522)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use last completed automatic backup time instead of last available backup * Update ha-backup-overview-summary.ts * Update src/panels/config/backup/components/overview/ha-backup-overview-summary.ts * Update ha-config-backup-overview.ts --- .../overview/ha-backup-overview-summary.ts | 28 +++++++++++-------- .../backup/ha-config-backup-overview.ts | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts index 198797e6a6..e6e4d78195 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-summary.ts @@ -84,21 +84,21 @@ class HaBackupOverviewBackups extends LitElement { const lastSuccessfulBackup = this._lastSuccessfulBackup(this.backups); - const lastSuccessfulBackupDate = lastSuccessfulBackup - ? new Date(lastSuccessfulBackup.date) - : new Date(0); - const lastAttempt = this.config.last_attempted_automatic_backup ? new Date(this.config.last_attempted_automatic_backup) : undefined; + const lastCompletedBackupDate = this.config.last_completed_automatic_backup + ? new Date(this.config.last_completed_automatic_backup) + : undefined; + const now = new Date(); const lastBackupDescription = lastSuccessfulBackup - ? `Last successful backup ${relativeTime(lastSuccessfulBackupDate, this.hass.locale, now, true)} and stored in ${lastSuccessfulBackup.agent_ids?.length} locations.` + ? `Last successful backup ${relativeTime(new Date(lastSuccessfulBackup.date), this.hass.locale, now, true)} and stored in ${lastSuccessfulBackup.agent_ids?.length} locations.` : "You have no successful backups."; - if (lastAttempt && lastAttempt > lastSuccessfulBackupDate) { + if (lastAttempt && lastAttempt > (lastCompletedBackupDate || 0)) { const lastAttemptDescription = `The last automatic backup triggered ${relativeTime(lastAttempt, this.hass.locale, now, true)} wasn't successful.`; return html` + + + + ${nextBackupDescription} + + `; } - const nextBackupDescription = this._nextBackupDescription( - this.config.schedule.state - ); - const numberOfDays = differenceInDays( // Subtract a few hours to avoid showing as overdue if it's just a few hours (e.g. daylight saving) addHours(now, -OVERDUE_MARGIN_HOURS), - lastSuccessfulBackupDate + new Date(lastSuccessfulBackup.date) ); const isOverdue = diff --git a/src/panels/config/backup/ha-config-backup-overview.ts b/src/panels/config/backup/ha-config-backup-overview.ts index 0a132b799f..bc86bc8cc2 100644 --- a/src/panels/config/backup/ha-config-backup-overview.ts +++ b/src/panels/config/backup/ha-config-backup-overview.ts @@ -127,7 +127,7 @@ class HaConfigBackupOverview extends LitElement { } private get _needsOnboarding() { - return !this.config?.create_backup.password; + return this.config && !this.config.create_backup.password; } protected render(): TemplateResult { From 806cc2c608dfa1bdee0a43d2917b271d34657643 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 31 Dec 2024 17:02:57 +0100 Subject: [PATCH 28/53] Fix automation traces (#23524) --- src/common/array/ensure-array.ts | 13 +++++++++---- src/components/trace/hat-graph-node.ts | 6 +++--- src/components/trace/hat-script-graph.ts | 6 +++--- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/common/array/ensure-array.ts b/src/common/array/ensure-array.ts index 360024f6ab..dadfa73235 100644 --- a/src/common/array/ensure-array.ts +++ b/src/common/array/ensure-array.ts @@ -1,14 +1,19 @@ -type NonUndefined = T extends undefined ? never : T; +type NonNullUndefined = T extends undefined + ? never + : T extends null + ? never + : T; /** * Ensure that the input is an array or wrap it in an array * @param value - The value to ensure is an array */ export function ensureArray(value: undefined): undefined; -export function ensureArray(value: T | T[]): NonUndefined[]; -export function ensureArray(value: T | readonly T[]): NonUndefined[]; +export function ensureArray(value: null): null; +export function ensureArray(value: T | T[]): NonNullUndefined[]; +export function ensureArray(value: T | readonly T[]): NonNullUndefined[]; export function ensureArray(value) { - if (value === undefined || Array.isArray(value)) { + if (value === undefined || value === null || Array.isArray(value)) { return value; } return [value]; diff --git a/src/components/trace/hat-graph-node.ts b/src/components/trace/hat-graph-node.ts index de9e765607..994f1dc68a 100644 --- a/src/components/trace/hat-graph-node.ts +++ b/src/components/trace/hat-graph-node.ts @@ -20,8 +20,8 @@ export class HatGraphNode extends LitElement { @property({ attribute: false, reflect: true, type: Boolean }) notEnabled = false; - @property({ attribute: false, reflect: true, type: Boolean }) graphStart = - false; + @property({ attribute: "graph-start", reflect: true, type: Boolean }) + graphStart = false; @property({ type: Boolean, attribute: "nofocus" }) noFocus = false; @@ -112,7 +112,7 @@ export class HatGraphNode extends LitElement { var(--hat-graph-node-size) + var(--hat-graph-spacing) + 1px ); } - :host([graphStart]) { + :host([graph-start]) { height: calc(var(--hat-graph-node-size) + 2px); } :host([track]) { diff --git a/src/components/trace/hat-script-graph.ts b/src/components/trace/hat-script-graph.ts index e0e9c1f7d1..1bc7f057c1 100644 --- a/src/components/trace/hat-script-graph.ts +++ b/src/components/trace/hat-script-graph.ts @@ -91,7 +91,7 @@ export class HatScriptGraph extends LitElement { } return html`
Date: Tue, 31 Dec 2024 20:23:30 +0100 Subject: [PATCH 29/53] Bumped version to 20241231.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9aab346d0b..68253ece5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241230.0" +version = "20241231.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From a7ef498d75ba782ec1690b426bb63a2106355d6e Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 31 Dec 2024 22:39:50 +0100 Subject: [PATCH 30/53] Handle no cloud subscription better in backups (#23523) --- .../config/ha-backup-config-agents.ts | 17 +++++++++++++++-- .../backup/dialogs/dialog-generate-backup.ts | 5 ++++- .../backup/dialogs/dialog-upload-backup.ts | 1 - 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/panels/config/backup/components/config/ha-backup-config-agents.ts b/src/panels/config/backup/components/config/ha-backup-config-agents.ts index de74488dcf..3a1706f3d4 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-agents.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-agents.ts @@ -51,6 +51,9 @@ class HaBackupConfigAgents extends LitElement { private _description(agentId: string) { if (agentId === CLOUD_AGENT) { + if (this.cloudStatus.logged_in && !this.cloudStatus.active_subscription) { + return "You currently do not have an active Home Assistant Cloud subscription."; + } return "Note: It stores only one backup with a maximum size of 5 GB, regardless of your settings."; } if (isNetworkMountAgent(agentId)) { @@ -72,6 +75,10 @@ class HaBackupConfigAgents extends LitElement { this._agentIds ); const description = this._description(agentId); + const noCloudSubscription = + agentId === CLOUD_AGENT && + this.cloudStatus.logged_in && + !this.cloudStatus.active_subscription; return html` ${isLocalAgent(agentId) @@ -107,7 +114,9 @@ class HaBackupConfigAgents extends LitElement { @@ -133,7 +142,11 @@ class HaBackupConfigAgents extends LitElement { // Ensure we don't have duplicates, agents exist in the list and cloud is logged in this.value = [...new Set(this.value)] .filter((agent) => this._agentIds.some((id) => id === agent)) - .filter((id) => id !== CLOUD_AGENT || this.cloudStatus.logged_in); + .filter( + (id) => + id !== CLOUD_AGENT || + (this.cloudStatus.logged_in && this.cloudStatus.active_subscription) + ); fireEvent(this, "value-changed", { value: this.value }); } diff --git a/src/panels/config/backup/dialogs/dialog-generate-backup.ts b/src/panels/config/backup/dialogs/dialog-generate-backup.ts index 866aaf441d..e6185d4858 100644 --- a/src/panels/config/backup/dialogs/dialog-generate-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-generate-backup.ts @@ -100,7 +100,10 @@ class DialogGenerateBackup extends LitElement implements HassDialog { this._agentIds = agents .map((agent) => agent.agent_id) .filter( - (id) => id !== CLOUD_AGENT || this._params?.cloudStatus?.logged_in + (id) => + id !== CLOUD_AGENT || + (this._params?.cloudStatus?.logged_in && + this._params?.cloudStatus?.active_subscription) ) .sort(compareAgents); } diff --git a/src/panels/config/backup/dialogs/dialog-upload-backup.ts b/src/panels/config/backup/dialogs/dialog-upload-backup.ts index a9b3e9262c..ff3e454f22 100644 --- a/src/panels/config/backup/dialogs/dialog-upload-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-upload-backup.ts @@ -20,7 +20,6 @@ import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; import { haStyle, haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import { showAlertDialog } from "../../../lovelace/custom-card-helpers"; -import "../components/ha-backup-agents-picker"; import type { UploadBackupDialogParams } from "./show-dialog-upload-backup"; const SUPPORTED_FORMAT = "application/x-tar"; From 7e80eed003f961f1070e40ccebabeaab57fd2964 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 2 Jan 2025 04:46:36 -0800 Subject: [PATCH 31/53] Display an error if saving new script times out (#23527) * Display an error if saving new automation times out * changes * update * string tweak * Fix save failed for scripts --- .../config/automation/ha-automation-editor.ts | 7 ++- src/panels/config/script/ha-script-editor.ts | 55 +++++++++++++++---- src/translations/en.json | 4 +- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index e6ca397584..05df54facf 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -965,6 +965,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin( type: this.hass.localize( "ui.panel.config.automation.editor.type_automation" ), + types: this.hass.localize( + "ui.panel.config.automation.editor.type_automation_plural" + ), } ), warning: true, @@ -992,9 +995,9 @@ export class HaAutomationEditor extends PreventUnsavedMixin( navigate(`/config/automation/edit/${id}`, { replace: true }); } } catch (errors: any) { - this._errors = errors.body.message || errors.error || errors.body; + this._errors = errors.body?.message || errors.error || errors.body; showToast(this, { - message: errors.body.message || errors.error || errors.body, + message: errors.body?.message || errors.error || errors.body, }); throw errors; } finally { diff --git a/src/panels/config/script/ha-script-editor.ts b/src/panels/config/script/ha-script-editor.ts index c1d9bc39ca..8f7a639a32 100644 --- a/src/panels/config/script/ha-script-editor.ts +++ b/src/panels/config/script/ha-script-editor.ts @@ -26,6 +26,7 @@ import { navigate } from "../../../common/navigate"; import { slugify } from "../../../common/string/slugify"; import { computeRTL } from "../../../common/util/compute_rtl"; import { afterNextRender } from "../../../common/util/render-status"; +import { promiseTimeout } from "../../../common/util/promise-timeout"; import "../../../components/ha-button-menu"; import "../../../components/ha-fab"; @@ -915,17 +916,49 @@ export class HaScriptEditor extends SubscribeMixin( // wait for new script to appear in entity registry if (entityRegPromise) { - const script = await entityRegPromise; - entityId = script.entity_id; + try { + const script = await promiseTimeout(2000, entityRegPromise); + entityId = script.entity_id; + } catch (e) { + entityId = undefined; + if (e instanceof Error && e.name === "TimeoutError") { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.automation.editor.new_automation_setup_failed_title", + { + type: this.hass.localize( + "ui.panel.config.automation.editor.type_script" + ), + } + ), + text: this.hass.localize( + "ui.panel.config.automation.editor.new_automation_setup_failed_text", + { + type: this.hass.localize( + "ui.panel.config.automation.editor.type_script" + ), + types: this.hass.localize( + "ui.panel.config.automation.editor.type_script_plural" + ), + } + ), + warning: true, + }); + } else { + throw e; + } + } } - await updateEntityRegistryEntry(this.hass, entityId!, { - categories: { - script: this._entityRegistryUpdate.category || null, - }, - labels: this._entityRegistryUpdate.labels || [], - area_id: this._entityRegistryUpdate.area || null, - }); + if (entityId) { + await updateEntityRegistryEntry(this.hass, entityId, { + categories: { + script: this._entityRegistryUpdate.category || null, + }, + labels: this._entityRegistryUpdate.labels || [], + area_id: this._entityRegistryUpdate.area || null, + }); + } } this._dirty = false; @@ -934,9 +967,9 @@ export class HaScriptEditor extends SubscribeMixin( navigate(`/config/script/edit/${id}`, { replace: true }); } } catch (errors: any) { - this._errors = errors.body.message || errors.error || errors.body; + this._errors = errors.body?.message || errors.error || errors.body; showToast(this, { - message: errors.body.message || errors.error || errors.body, + message: errors.body?.message || errors.error || errors.body, }); throw errors; } finally { diff --git a/src/translations/en.json b/src/translations/en.json index 09a80f5f99..01f4d1436a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3067,8 +3067,10 @@ "switch_ui_yaml_error": "There are currently YAML errors in the automation, and it cannot be parsed. Switching to UI mode may cause pending changes to be lost. Press cancel to correct any errors before proceeding to prevent loss of pending changes, or continue if you are sure.", "type_automation": "automation", "type_script": "script", + "type_automation_plural": "[%key:ui::panel::config::blueprint::overview::types_plural::automation%]", + "type_script_plural": "[%key:ui::panel::config::blueprint::overview::types_plural::script%]", "new_automation_setup_failed_title": "New {type} setup failed", - "new_automation_setup_failed_text": "Your new {type} has saved, but waiting for it to setup has timed out. This could be due to errors parsing your configuration.yaml, please check the configuration in developer tools. Your {type} will not be visible until this is corrected, and automations are reloaded. Changes to area, category, or labels were not saved and must be reapplied.", + "new_automation_setup_failed_text": "Your new {type} has saved, but waiting for it to setup has timed out. This could be due to errors parsing your configuration.yaml, please check the configuration in developer tools. Your {type} will not be visible until this is corrected, and {types} are reloaded. Changes to area, category, or labels were not saved and must be reapplied.", "triggers": { "name": "Triggers", "header": "When", From 3b8bc242fe3b80174c7dbfdd99e0fa51232aa4e5 Mon Sep 17 00:00:00 2001 From: Philipp <84805847+insomniac2305@users.noreply.github.com> Date: Thu, 2 Jan 2025 16:03:04 +0100 Subject: [PATCH 32/53] Fix media management delete button misalignment (#23534) --- src/components/media-player/dialog-media-manage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/media-player/dialog-media-manage.ts b/src/components/media-player/dialog-media-manage.ts index a1a57933da..543adb3aa5 100644 --- a/src/components/media-player/dialog-media-manage.ts +++ b/src/components/media-player/dialog-media-manage.ts @@ -117,7 +117,7 @@ class DialogMediaManage extends LitElement { : html` Date: Thu, 2 Jan 2025 15:56:03 +0100 Subject: [PATCH 33/53] Changes to the valueText should also rescale ha-gauge text (#23536) Changes to the valueText should also recenter ha-gauge text --- src/components/ha-gauge.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ha-gauge.ts b/src/components/ha-gauge.ts index cf38f90c70..9870e30ae7 100644 --- a/src/components/ha-gauge.ts +++ b/src/components/ha-gauge.ts @@ -62,6 +62,7 @@ export class HaGauge extends LitElement { if ( !this._updated || (!changedProperties.has("value") && + !changedProperties.has("valueText") && !changedProperties.has("label") && !changedProperties.has("_segment_label")) ) { From 486038c426f4cfbe5fec96a3e5b96a4de25ead90 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 13:44:04 +0100 Subject: [PATCH 34/53] Add space for the fab on datatable without tabs (#23545) Add space for the fab on backups datatable --- src/components/data-table/ha-data-table.ts | 9 ++++++++- src/layouts/hass-tabs-subpage.ts | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index 4278562171..af1f1f358b 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -515,7 +515,7 @@ export class HaDataTable extends LitElement { return html`
${row.content}
`; } if (row.empty) { - return html`
`; + return html`
`; } return html`
Date: Thu, 2 Jan 2025 13:44:34 +0100 Subject: [PATCH 35/53] Fix copy on button to clear the selected background image (#23546) --- src/components/ha-picture-upload.ts | 2 +- src/translations/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ha-picture-upload.ts b/src/components/ha-picture-upload.ts index 8053151cdb..09796ea3e1 100644 --- a/src/components/ha-picture-upload.ts +++ b/src/components/ha-picture-upload.ts @@ -95,7 +95,7 @@ export class HaPictureUpload extends LitElement { diff --git a/src/translations/en.json b/src/translations/en.json index 01f4d1436a..e5416b9b6f 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -756,7 +756,7 @@ }, "picture-upload": { "label": "Add picture", - "change_picture": "Change picture", + "clear_picture": "Clear picture", "current_image_alt": "Current picture", "supported_formats": "Supports JPEG, PNG, or GIF image.", "unsupported_format": "Unsupported format, please choose a JPEG, PNG, or GIF image.", From 01bc45c78b89d41f0f4f5c6e648d03d3facc9f7b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 15:35:22 +0100 Subject: [PATCH 36/53] Backup text updates (#23547) --- .../components/overview/ha-backup-overview-onboarding.ts | 5 +---- .../components/overview/ha-backup-overview-settings.ts | 8 ++++---- .../config/backup/dialogs/dialog-backup-onboarding.ts | 2 +- .../backup/dialogs/show-dialog-backup_onboarding.ts | 1 + src/panels/config/backup/ha-config-backup-backups.ts | 2 +- src/panels/config/backup/ha-config-backup-details.ts | 8 ++++++++ src/panels/config/backup/ha-config-backup-overview.ts | 5 +++-- src/panels/config/backup/ha-config-backup-settings.ts | 2 +- 8 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-onboarding.ts b/src/panels/config/backup/components/overview/ha-backup-overview-onboarding.ts index ebce3ea554..a094cad22d 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-onboarding.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-onboarding.ts @@ -37,10 +37,7 @@ class HaBackupOverviewBackups extends LitElement {

Backups are essential for a reliable smart home. They help protect the work you've put into setting up your smart home, and if the - worst happens, you can get back up and running quickly. It is - recommended that you create a backup every day. You should keep - three backups in at least two different locations, one of which - should be off-site. + worst happens, you can get back up and running quickly.

diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts b/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts index 5936937b19..3b3f56cdfb 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-settings.ts @@ -34,7 +34,7 @@ class HaBackupBackupsSummary extends LitElement { const { state: schedule } = config.schedule; if (schedule === BackupScheduleState.NEVER) { - return "Automatic backups are disabled"; + return "Automatic backups are not scheduled"; } let copiesText = "and keep all backups"; @@ -116,7 +116,7 @@ class HaBackupBackupsSummary extends LitElement { return html` -
Automatic backups
+
Backup settings
- Schedule and number of backups to keep + Automatic backup schedule and retention
@@ -174,7 +174,7 @@ class HaBackupBackupsSummary extends LitElement {
- Configure automatic backups + Configure backup settings
diff --git a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts index cb5b798c50..01f881de58 100644 --- a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts +++ b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts @@ -90,7 +90,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { public showDialog(params: BackupOnboardingDialogParams): void { this._params = params; - this._step = STEPS[0]; + this._step = params.skipWelcome ? STEPS[1] : STEPS[0]; this._config = RECOMMENDED_CONFIG; const agents: string[] = []; diff --git a/src/panels/config/backup/dialogs/show-dialog-backup_onboarding.ts b/src/panels/config/backup/dialogs/show-dialog-backup_onboarding.ts index c411d6f0d7..0602077fc1 100644 --- a/src/panels/config/backup/dialogs/show-dialog-backup_onboarding.ts +++ b/src/panels/config/backup/dialogs/show-dialog-backup_onboarding.ts @@ -5,6 +5,7 @@ export interface BackupOnboardingDialogParams { submit?: (value: boolean) => void; cancel?: () => void; cloudStatus?: CloudStatus; + skipWelcome?: boolean; } const loadDialog = () => import("./dialog-backup-onboarding"); diff --git a/src/panels/config/backup/ha-config-backup-backups.ts b/src/panels/config/backup/ha-config-backup-backups.ts index a0675e70b8..18d65557f0 100644 --- a/src/panels/config/backup/ha-config-backup-backups.ts +++ b/src/panels/config/backup/ha-config-backup-backups.ts @@ -304,7 +304,7 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) { has-fab .tabs=${[ { - translationKey: "ui.panel.config.backup.caption", + name: "My backups", path: `/config/backup/list`, }, ]} diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index 248913fe5e..8c42bfe69f 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -143,6 +143,14 @@ class HaConfigBackupDetails extends LitElement { )} Created + + + ${this._backup.protected + ? "Encrypted AES-128" + : "Not encrypted"} + + Protected +
diff --git a/src/panels/config/backup/ha-config-backup-overview.ts b/src/panels/config/backup/ha-config-backup-overview.ts index bc86bc8cc2..8080aef77b 100644 --- a/src/panels/config/backup/ha-config-backup-overview.ts +++ b/src/panels/config/backup/ha-config-backup-overview.ts @@ -73,12 +73,13 @@ class HaConfigBackupOverview extends LitElement { private _handleOnboardingButtonClick(ev) { ev.stopPropagation(); - this._setupAutomaticBackup(); + this._setupAutomaticBackup(true); } - private async _setupAutomaticBackup() { + private async _setupAutomaticBackup(skipWelcome = false) { const success = await showBackupOnboardingDialog(this, { cloudStatus: this.cloudStatus, + skipWelcome, }); if (!success) { return; diff --git a/src/panels/config/backup/ha-config-backup-settings.ts b/src/panels/config/backup/ha-config-backup-settings.ts index e4b7326ada..bb3a5f5c5f 100644 --- a/src/panels/config/backup/ha-config-backup-settings.ts +++ b/src/panels/config/backup/ha-config-backup-settings.ts @@ -91,7 +91,7 @@ class HaConfigBackupSettings extends LitElement { back-path="/config/backup" .hass=${this.hass} .narrow=${this.narrow} - .header=${"Automatic backups"} + .header=${"Backup settings"} >
From 64ad37ed6acd41ae28d0234af63ae7540eb1f80f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 15:13:53 +0100 Subject: [PATCH 37/53] Update change encryption key dialog (#23551) --- .../dialog-change-backup-encryption-key.ts | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts b/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts index 3e140c60f7..c95fc077c1 100644 --- a/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts +++ b/src/panels/config/backup/dialogs/dialog-change-backup-encryption-key.ts @@ -92,7 +92,9 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog { ? "Save current encryption key" : this._step === "new" ? "New encryption key" - : ""; + : this._step === "done" + ? "Save new encryption key" + : ""; return html` @@ -166,10 +168,22 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog { case "new": return html`

- Keep this encryption key in a safe place, as you will need it to - access your backup, allowing it to be restored. Either record the + All next backups will use the new encryption key. Encryption keeps + your backups private and secure. +

+
+

${this._newEncryptionKey}

+ +
+ `; + case "done": + return html`

+ Keep this new encryption key in a safe place, as you will need it to + access your backups, allowing it to be restored. Either record the characters below or download them as an emergency kit file. - Encryption keeps your backups private and secure.

${this._newEncryptionKey}

@@ -189,18 +203,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog { Download - - `; - case "done": - return html` -
- Casita Home Assistant logo -

Encryption key changed

-
- `; + `; } return nothing; } @@ -303,13 +306,6 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog { p { margin-top: 0; } - .done { - text-align: center; - font-size: 22px; - font-style: normal; - font-weight: 400; - line-height: 28px; - } `, ]; } From be967940a2d967e5c5ecd2a1d6409e80277f238c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 15:13:42 +0100 Subject: [PATCH 38/53] Add warning when no backup location is selected (#23550) * Add warning when no backup location is selected * Move to bottom --- src/panels/config/backup/ha-config-backup-settings.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/panels/config/backup/ha-config-backup-settings.ts b/src/panels/config/backup/ha-config-backup-settings.ts index bb3a5f5c5f..8ec6ff1257 100644 --- a/src/panels/config/backup/ha-config-backup-settings.ts +++ b/src/panels/config/backup/ha-config-backup-settings.ts @@ -8,6 +8,7 @@ import { nextRender } from "../../../common/util/render-status"; import "../../../components/ha-button"; import "../../../components/ha-card"; import "../../../components/ha-icon-next"; +import "../../../components/ha-alert"; import "../../../components/ha-password-field"; import type { BackupConfig } from "../../../data/backup"; import { updateBackupConfig } from "../../../data/backup"; @@ -134,6 +135,14 @@ class HaConfigBackupSettings extends LitElement { .cloudStatus=${this.cloudStatus} @value-changed=${this._agentsConfigChanged} > + ${!this._config.create_backup.agent_ids.length + ? html`You have to select at least one location to create a + backup.
` + : nothing}
From e03dc2c382a2df259389f5a47fd88201cea53f75 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 16:04:17 +0100 Subject: [PATCH 39/53] Move local location backup setting (#23548) --- .../backup/ha-config-backup-overview.ts | 53 +++++-------------- .../backup/ha-config-backup-settings.ts | 37 +++++++++++++ src/translations/en.json | 2 +- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/src/panels/config/backup/ha-config-backup-overview.ts b/src/panels/config/backup/ha-config-backup-overview.ts index 8080aef77b..c48e3f0034 100644 --- a/src/panels/config/backup/ha-config-backup-overview.ts +++ b/src/panels/config/backup/ha-config-backup-overview.ts @@ -1,8 +1,7 @@ -import { mdiDotsVertical, mdiHarddisk, mdiPlus, mdiUpload } from "@mdi/js"; +import { mdiDotsVertical, mdiPlus, mdiUpload } from "@mdi/js"; import type { CSSResultGroup, TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; -import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { fireEvent } from "../../../common/dom/fire_event"; import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event"; import "../../../components/ha-button"; @@ -33,7 +32,6 @@ import "./components/overview/ha-backup-overview-settings"; import "./components/overview/ha-backup-overview-summary"; import { showBackupOnboardingDialog } from "./dialogs/show-dialog-backup_onboarding"; import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup"; -import { showLocalBackupLocationDialog } from "./dialogs/show-dialog-local-backup-location"; import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup"; import { showUploadBackupDialog } from "./dialogs/show-dialog-upload-backup"; @@ -63,14 +61,6 @@ class HaConfigBackupOverview extends LitElement { await showUploadBackupDialog(this, {}); } - private async _changeLocalLocation(ev) { - if (!shouldHandleRequestSelectedEvent(ev)) { - return; - } - - showLocalBackupLocationDialog(this, {}); - } - private _handleOnboardingButtonClick(ev) { ev.stopPropagation(); this._setupAutomaticBackup(true); @@ -135,8 +125,6 @@ class HaConfigBackupOverview extends LitElement { const backupInProgress = "state" in this.manager && this.manager.state === "in_progress"; - const isHassio = isComponentLoaded(this.hass, "hassio"); - return html` -
- - - ${isHassio - ? html` - - Change local location - ` - : nothing} - - - Upload backup - - -
+ + + + + Upload backup + +
${backupInProgress ? html` diff --git a/src/panels/config/backup/ha-config-backup-settings.ts b/src/panels/config/backup/ha-config-backup-settings.ts index 8ec6ff1257..f581f5cd92 100644 --- a/src/panels/config/backup/ha-config-backup-settings.ts +++ b/src/panels/config/backup/ha-config-backup-settings.ts @@ -1,15 +1,21 @@ +import { mdiDotsVertical, mdiHarddisk } from "@mdi/js"; import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { fireEvent } from "../../../common/dom/fire_event"; +import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event"; import { debounce } from "../../../common/util/debounce"; import { nextRender } from "../../../common/util/render-status"; import "../../../components/ha-button"; +import "../../../components/ha-button-menu"; import "../../../components/ha-card"; +import "../../../components/ha-icon-button"; import "../../../components/ha-icon-next"; +import "../../../components/ha-list-item"; import "../../../components/ha-alert"; import "../../../components/ha-password-field"; +import "../../../components/ha-svg-icon"; import type { BackupConfig } from "../../../data/backup"; import { updateBackupConfig } from "../../../data/backup"; import type { CloudStatus } from "../../../data/cloud"; @@ -21,6 +27,7 @@ import type { BackupConfigData } from "./components/config/ha-backup-config-data import "./components/config/ha-backup-config-encryption-key"; import "./components/config/ha-backup-config-schedule"; import type { BackupConfigSchedule } from "./components/config/ha-backup-config-schedule"; +import { showLocalBackupLocationDialog } from "./dialogs/show-dialog-local-backup-location"; @customElement("ha-config-backup-settings") class HaConfigBackupSettings extends LitElement { @@ -94,6 +101,28 @@ class HaConfigBackupSettings extends LitElement { .narrow=${this.narrow} .header=${"Backup settings"} > + ${isComponentLoaded(this.hass, "hassio") + ? html` + + + + + Change default action location + + + ` + : nothing} +
Automatic backups
@@ -166,6 +195,14 @@ class HaConfigBackupSettings extends LitElement { `; } + private async _changeLocalLocation(ev) { + if (!shouldHandleRequestSelectedEvent(ev)) { + return; + } + + showLocalBackupLocationDialog(this, {}); + } + private _scheduleConfigChanged(ev) { const value = ev.detail.value as BackupConfigSchedule; this._config = { diff --git a/src/translations/en.json b/src/translations/en.json index e5416b9b6f..688df34b20 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2215,7 +2215,7 @@ }, "dialogs": { "local_backup_location": { - "title": "Change local backup location", + "title": "Change default local backup location", "description": "Change the default location where local backups are stored on your Home Assistant instance.", "note": "This location will be used when you create a backup using the supervisor actions in an automation for example.", "options": { From fcc9da6d85037e9f94d16ac96c1233ee8410ce20 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 15:38:11 +0100 Subject: [PATCH 40/53] Backup with db requires config, disabled next if no data is selected (#23549) --- .../config/ha-backup-config-data.ts | 8 +++---- .../backup/dialogs/dialog-generate-backup.ts | 20 ++++++++++++++++- .../backup/ha-config-backup-overview.ts | 22 ++++++++++--------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/panels/config/backup/components/config/ha-backup-config-data.ts b/src/panels/config/backup/components/config/ha-backup-config-data.ts index be462ff3e5..5c3dce968b 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-data.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-data.ts @@ -138,7 +138,8 @@ class HaBackupConfigData extends LitElement { const include_addons = data.addons_mode === "custom" ? data.addons : []; this.value = { - include_homeassistant: data.homeassistant || this.forceHomeAssistant, + include_homeassistant: + data.homeassistant || data.database || this.forceHomeAssistant, include_addons: include_addons.length ? include_addons : undefined, include_all_addons: data.addons_mode === "all", include_database: data.database, @@ -168,7 +169,7 @@ class HaBackupConfigData extends LitElement { slot="end" @change=${this._switchChanged} .checked=${data.homeassistant} - .disabled=${this.forceHomeAssistant} + .disabled=${this.forceHomeAssistant || data.database} > @@ -296,7 +297,6 @@ class HaBackupConfigData extends LitElement { ...data, [target.id]: target.checked, }); - fireEvent(this, "value-changed", { value: this.value }); } private _selectChanged(ev: Event) { @@ -309,7 +309,6 @@ class HaBackupConfigData extends LitElement { if (target.id === "addons_mode") { this._showAddons = target.value === "custom"; } - fireEvent(this, "value-changed", { value: this.value }); } private _addonsChanged(ev: CustomEvent) { @@ -320,7 +319,6 @@ class HaBackupConfigData extends LitElement { ...data, addons, }); - fireEvent(this, "value-changed", { value: this.value }); } static styles = css` diff --git a/src/panels/config/backup/dialogs/dialog-generate-backup.ts b/src/panels/config/backup/dialogs/dialog-generate-backup.ts index e6185d4858..b8c64c1255 100644 --- a/src/panels/config/backup/dialogs/dialog-generate-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-generate-backup.ts @@ -209,12 +209,30 @@ class DialogGenerateBackup extends LitElement implements HassDialog { Create backup ` - : html`Next`} + : html`Next`}
`; } + private get _noDataSelected() { + const hassio = isComponentLoaded(this.hass, "hassio"); + if ( + this._formData?.data.include_homeassistant || + this._formData?.data.include_database || + (hassio && this._formData?.data.include_folders?.length) || + (hassio && this._formData?.data.include_all_addons) || + (hassio && this._formData?.data.include_addons?.length) + ) { + return false; + } + return true; + } + private _renderData() { if (!this._formData) { return nothing; diff --git a/src/panels/config/backup/ha-config-backup-overview.ts b/src/panels/config/backup/ha-config-backup-overview.ts index c48e3f0034..26e86a2105 100644 --- a/src/panels/config/backup/ha-config-backup-overview.ts +++ b/src/panels/config/backup/ha-config-backup-overview.ts @@ -160,22 +160,24 @@ class HaConfigBackupOverview extends LitElement { > ` - : html` - - - `} + : this.config + ? html` + + + ` + : nothing} - ${!this._needsOnboarding + ${!this._needsOnboarding && this.config ? html` Date: Thu, 2 Jan 2025 16:12:31 +0100 Subject: [PATCH 41/53] Add show encryption key dialog (#23552) --- .../config/ha-backup-config-encryption-key.ts | 13 +- .../dialog-show-backup-encryption-key.ts | 174 ++++++++++++++++++ .../show-dialog-show-backup-encryption-key.ts | 17 ++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts create mode 100644 src/panels/config/backup/dialogs/show-dialog-show-backup-encryption-key.ts diff --git a/src/panels/config/backup/components/config/ha-backup-config-encryption-key.ts b/src/panels/config/backup/components/config/ha-backup-config-encryption-key.ts index 0298a6c5bf..4254215d55 100644 --- a/src/panels/config/backup/components/config/ha-backup-config-encryption-key.ts +++ b/src/panels/config/backup/components/config/ha-backup-config-encryption-key.ts @@ -9,6 +9,7 @@ import { showChangeBackupEncryptionKeyDialog } from "../../dialogs/show-dialog-c import { showSetBackupEncryptionKeyDialog } from "../../dialogs/show-dialog-set-backup-encryption-key"; import { downloadEmergencyKit } from "../../../../../data/backup"; +import { showShowBackupEncryptionKeyDialog } from "../../dialogs/show-dialog-show-backup-encryption-key"; @customElement("ha-backup-config-encryption-key") class HaBackupConfigEncryptionKey extends LitElement { @@ -34,7 +35,13 @@ class HaBackupConfigEncryptionKey extends LitElement { Download - + + Show my encryption key + + Please keep your encryption key private. + + Show + Change encryption key @@ -68,6 +75,10 @@ class HaBackupConfigEncryptionKey extends LitElement { downloadEmergencyKit(this.hass, this._value); } + private _show() { + showShowBackupEncryptionKeyDialog(this, { currentKey: this._value }); + } + private _change() { showChangeBackupEncryptionKeyDialog(this, { currentKey: this._value, diff --git a/src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts b/src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts new file mode 100644 index 0000000000..5861ad35a7 --- /dev/null +++ b/src/panels/config/backup/dialogs/dialog-show-backup-encryption-key.ts @@ -0,0 +1,174 @@ +import { mdiClose, mdiContentCopy, mdiDownload } from "@mdi/js"; +import type { CSSResultGroup } from "lit"; +import { LitElement, css, html, nothing } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { copyToClipboard } from "../../../../common/util/copy-clipboard"; +import "../../../../components/ha-button"; +import "../../../../components/ha-dialog-header"; +import "../../../../components/ha-icon-button"; +import "../../../../components/ha-icon-button-prev"; +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 "../../../../components/ha-password-field"; +import { downloadEmergencyKit } from "../../../../data/backup"; +import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; +import { haStyle, haStyleDialog } from "../../../../resources/styles"; +import type { HomeAssistant } from "../../../../types"; +import { showToast } from "../../../../util/toast"; +import type { ShowBackupEncryptionKeyDialogParams } from "./show-dialog-show-backup-encryption-key"; + +@customElement("ha-dialog-show-backup-encryption-key") +class DialogShowBackupEncryptionKey extends LitElement implements HassDialog { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _params?: ShowBackupEncryptionKeyDialogParams; + + @query("ha-md-dialog") private _dialog!: HaMdDialog; + + public showDialog(params: ShowBackupEncryptionKeyDialogParams): void { + this._params = params; + } + + public closeDialog(): void { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + private _closeDialog() { + this._dialog.close(); + } + + protected render() { + if (!this._params) { + return nothing; + } + + return html` + + + + Encryption key + +
+

+ Make sure you save the encryption key in a secure place so always + have access to your backups. +

+
+

${this._params?.currentKey}

+ +
+ + + Download emergency kit + + We recommend saving this encryption key file somewhere secure. + + + + Download + + + +
+
+ Close +
+
+ `; + } + + private async _copyKeyToClipboard() { + if (!this._params?.currentKey) { + return; + } + await copyToClipboard( + this._params?.currentKey, + this.renderRoot.querySelector("div")! + ); + showToast(this, { + message: this.hass.localize("ui.common.copied_clipboard"), + }); + } + + private _download() { + if (!this._params?.currentKey) { + return; + } + downloadEmergencyKit(this.hass, this._params.currentKey, "old"); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + haStyleDialog, + css` + ha-md-dialog { + width: 90vw; + max-width: 560px; + --dialog-content-padding: 8px 24px; + } + ha-md-list { + background: none; + --md-list-item-leading-space: 0; + --md-list-item-trailing-space: 0; + } + ha-button.danger { + --mdc-theme-primary: var(--error-color); + } + .encryption-key { + border: 1px solid var(--divider-color); + background-color: var(--primary-background-color); + border-radius: 8px; + padding: 16px; + display: flex; + flex-direction: row; + align-items: center; + gap: 24px; + } + .encryption-key p { + margin: 0; + flex: 1; + font-family: "Roboto Mono", "Consolas", "Menlo", monospace; + font-size: 20px; + font-style: normal; + font-weight: 400; + line-height: 28px; + text-align: center; + } + .encryption-key ha-icon-button { + flex: none; + margin: -16px; + } + @media all and (max-width: 450px), all and (max-height: 500px) { + ha-md-dialog { + max-width: none; + } + div[slot="content"] { + margin-top: 0; + } + } + p { + margin-top: 0; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-dialog-show-backup-encryption-key": DialogShowBackupEncryptionKey; + } +} diff --git a/src/panels/config/backup/dialogs/show-dialog-show-backup-encryption-key.ts b/src/panels/config/backup/dialogs/show-dialog-show-backup-encryption-key.ts new file mode 100644 index 0000000000..e69d7ae774 --- /dev/null +++ b/src/panels/config/backup/dialogs/show-dialog-show-backup-encryption-key.ts @@ -0,0 +1,17 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; + +export interface ShowBackupEncryptionKeyDialogParams { + currentKey: string; +} + +const loadDialog = () => import("./dialog-show-backup-encryption-key"); + +export const showShowBackupEncryptionKeyDialog = ( + element: HTMLElement, + params?: ShowBackupEncryptionKeyDialogParams +) => + fireEvent(element, "show-dialog", { + dialogTag: "ha-dialog-show-backup-encryption-key", + dialogImport: loadDialog, + dialogParams: params, + }); From 8f58681d83f6243bef1d28c198ebcba25944caa5 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 16:11:50 +0100 Subject: [PATCH 42/53] always zoom timeline charts on x axis (#23554) --- src/components/chart/ha-chart-base.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index c3aab89e82..017a5dab07 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -433,7 +433,10 @@ export class HaChartBase extends LitElement { speed: 0.05, }, mode: - (this.options?.scales?.y as any)?.type === "category" ? "y" : "x", + this.chartType !== "timeline" && + (this.options?.scales?.y as any)?.type === "category" + ? "y" + : "x", onZoomComplete: () => { const isZoomed = this.chart?.isZoomedOrPanned() ?? false; if (this._isZoomed && !isZoomed) { From 6c9df587e79501851fa42c977eb97f287725b5e0 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 16:18:11 +0100 Subject: [PATCH 43/53] Bumped version to 20250102.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 68253ece5b..bfbc1ef922 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241231.0" +version = "20250102.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 70953788ccb4103a99909e0028a3691249bceb60 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 16:54:35 +0100 Subject: [PATCH 44/53] Add back zopfli compression (#23555) --- build-scripts/gulp/compress.js | 61 +++++++++++++++++++++++------- package.json | 1 + yarn.lock | 69 +++++++++++++++++++++++++++++++--- 3 files changed, 112 insertions(+), 19 deletions(-) diff --git a/build-scripts/gulp/compress.js b/build-scripts/gulp/compress.js index e1fff793a6..81e1c87abe 100644 --- a/build-scripts/gulp/compress.js +++ b/build-scripts/gulp/compress.js @@ -3,6 +3,7 @@ import { constants } from "node:zlib"; import gulp from "gulp"; import brotli from "gulp-brotli"; +import zopfli from "gulp-zopfli-green"; import paths from "../paths.cjs"; const filesGlob = "*.{js,json,css,svg,xml}"; @@ -12,17 +13,18 @@ const brotliOptions = { [constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY, }, }; +const zopfliOptions = { threshold: 150 }; -const compressModern = (rootDir, modernDir) => +const compressModern = (rootDir, modernDir, compress) => gulp .src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], { base: rootDir, allowEmpty: true, }) - .pipe(brotli(brotliOptions)) + .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) .pipe(gulp.dest(rootDir)); -const compressOther = (rootDir, modernDir) => +const compressOther = (rootDir, modernDir, compress) => gulp .src( [ @@ -33,21 +35,52 @@ const compressOther = (rootDir, modernDir) => ], { base: rootDir, allowEmpty: true } ) - .pipe(brotli(brotliOptions)) + .pipe(compress === "zopfli" ? zopfli(zopfliOptions) : brotli(brotliOptions)) .pipe(gulp.dest(rootDir)); -const compressAppModern = () => - compressModern(paths.app_output_root, paths.app_output_latest); -const compressHassioModern = () => - compressModern(paths.hassio_output_root, paths.hassio_output_latest); +const compressAppModernBrotli = () => + compressModern(paths.app_output_root, paths.app_output_latest, "brotli"); +const compressAppModernZopfli = () => + compressModern(paths.app_output_root, paths.app_output_latest, "zopfli"); -const compressAppOther = () => - compressOther(paths.app_output_root, paths.app_output_latest); -const compressHassioOther = () => - compressOther(paths.hassio_output_root, paths.hassio_output_latest); +const compressHassioModernBrotli = () => + compressModern( + paths.hassio_output_root, + paths.hassio_output_latest, + "brotli" + ); +const compressHassioModernZopfli = () => + compressModern( + paths.hassio_output_root, + paths.hassio_output_latest, + "zopfli" + ); -gulp.task("compress-app", gulp.parallel(compressAppModern, compressAppOther)); +const compressAppOtherBrotli = () => + compressOther(paths.app_output_root, paths.app_output_latest, "brotli"); +const compressAppOtherZopfli = () => + compressOther(paths.app_output_root, paths.app_output_latest, "zopfli"); + +const compressHassioOtherBrotli = () => + compressOther(paths.hassio_output_root, paths.hassio_output_latest, "brotli"); +const compressHassioOtherZopfli = () => + compressOther(paths.hassio_output_root, paths.hassio_output_latest, "zopfli"); + +gulp.task( + "compress-app", + gulp.parallel( + compressAppModernBrotli, + compressAppOtherBrotli, + compressAppModernZopfli, + compressAppOtherZopfli + ) +); gulp.task( "compress-hassio", - gulp.parallel(compressHassioModern, compressHassioOther) + gulp.parallel( + compressHassioModernBrotli, + compressHassioOtherBrotli, + compressHassioModernZopfli, + compressHassioOtherZopfli + ) ); diff --git a/package.json b/package.json index bb9054c920..8e08c3ecce 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,7 @@ "element-internals-polyfill": "1.3.12", "fuse.js": "7.0.0", "google-timezones-json": "1.2.0", + "gulp-zopfli-green": "6.0.2", "hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch", "home-assistant-js-websocket": "9.4.0", "idb-keyval": "6.2.1", diff --git a/yarn.lock b/yarn.lock index 385d2ed0c4..e6e34683df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1801,6 +1801,15 @@ __metadata: languageName: node linkType: hard +"@gfx/zopfli@npm:^1.0.15": + version: 1.0.15 + resolution: "@gfx/zopfli@npm:1.0.15" + dependencies: + base64-js: "npm:^1.3.0" + checksum: 10/2721ad8c0cbbdac7d5ca9e01ad05f232b4e3cdcecf88f9b0ef9a2bdc7d05e1ca54ea68905430cf36338ef1077acec178aa7b258c67baa7a4c2b6d74067605723 + languageName: node + linkType: hard + "@gulpjs/messages@npm:^1.1.0": version: 1.1.0 resolution: "@gulpjs/messages@npm:1.1.0" @@ -5684,6 +5693,13 @@ __metadata: languageName: node linkType: hard +"any-promise@npm:^1.1.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10/6737469ba353b5becf29e4dc3680736b9caa06d300bda6548812a8fee63ae7d336d756f88572fa6b5219aed36698d808fa55f62af3e7e6845c7a1dc77d240edb + languageName: node + linkType: hard + "anymatch@npm:^3.1.3, anymatch@npm:~3.1.2": version: 3.1.3 resolution: "anymatch@npm:3.1.3" @@ -6030,7 +6046,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": +"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 10/669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -6237,7 +6253,7 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.2": +"bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" checksum: 10/a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388 @@ -7070,7 +7086,7 @@ __metadata: languageName: node linkType: hard -"defaults@npm:^1.0.3": +"defaults@npm:^1.0.3, defaults@npm:^1.0.4": version: 1.0.4 resolution: "defaults@npm:1.0.4" dependencies: @@ -8180,7 +8196,7 @@ __metadata: languageName: node linkType: hard -"fancy-log@npm:2.0.0": +"fancy-log@npm:2.0.0, fancy-log@npm:^2.0.0": version: 2.0.0 resolution: "fancy-log@npm:2.0.0" dependencies: @@ -8973,6 +8989,21 @@ __metadata: languageName: node linkType: hard +"gulp-zopfli-green@npm:6.0.2": + version: 6.0.2 + resolution: "gulp-zopfli-green@npm:6.0.2" + dependencies: + "@gfx/zopfli": "npm:^1.0.15" + bytes: "npm:^3.1.2" + defaults: "npm:^1.0.4" + fancy-log: "npm:^2.0.0" + plugin-error: "npm:^2.0.1" + stream-to-array: "npm:^2.3.0" + through2: "npm:^4.0.2" + checksum: 10/52e899dfb86777ff8f97a23af99c59e203ea485fbf04d0a8f4f1cfbd4d4c496808a3593ae8dac16584fc4b4d81cf127b2eda5355a61bcc213875c95cc86d41da + languageName: node + linkType: hard + "gulp@npm:5.0.0": version: 5.0.0 resolution: "gulp@npm:5.0.0" @@ -9254,6 +9285,7 @@ __metadata: gulp-brotli: "npm:3.0.0" gulp-json-transform: "npm:0.5.0" gulp-rename: "npm:2.0.0" + gulp-zopfli-green: "npm:6.0.2" hls.js: "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch" home-assistant-js-websocket: "npm:9.4.0" html-minifier-terser: "npm:7.2.0" @@ -12161,6 +12193,15 @@ __metadata: languageName: node linkType: hard +"plugin-error@npm:^2.0.1": + version: 2.0.1 + resolution: "plugin-error@npm:2.0.1" + dependencies: + ansi-colors: "npm:^1.0.1" + checksum: 10/9a4f91461cd24cce401112098969991d7aa6b4c94f78e0381234280c07da779570a8b21ab143292b534ec0117c09705a67e5d756c1c303d4706fdd7f861bf5bc + languageName: node + linkType: hard + "pngjs@npm:^3.0.0, pngjs@npm:^3.3.3": version: 3.4.0 resolution: "pngjs@npm:3.4.0" @@ -12383,7 +12424,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:2 || 3, readable-stream@npm:^3.0.6, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:2 || 3, readable-stream@npm:3, readable-stream@npm:^3.0.6, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -13566,6 +13607,15 @@ __metadata: languageName: node linkType: hard +"stream-to-array@npm:^2.3.0": + version: 2.3.0 + resolution: "stream-to-array@npm:2.3.0" + dependencies: + any-promise: "npm:^1.1.0" + checksum: 10/7feaf63b38399b850615e6ffcaa951e96e4c8f46745dbce4b553a94c5dc43966933813747014935a3ff97793e7f30a65270bde19f82b2932871a1879229a77cf + languageName: node + linkType: hard + "streamx@npm:^2.12.0, streamx@npm:^2.12.5, streamx@npm:^2.13.2, streamx@npm:^2.14.0": version: 2.21.1 resolution: "streamx@npm:2.21.1" @@ -13993,6 +14043,15 @@ __metadata: languageName: node linkType: hard +"through2@npm:^4.0.2": + version: 4.0.2 + resolution: "through2@npm:4.0.2" + dependencies: + readable-stream: "npm:3" + checksum: 10/72c246233d9a989bbebeb6b698ef0b7b9064cb1c47930f79b25d87b6c867e075432811f69b7b2ac8da00ca308191c507bdab913944be8019ac43b036ce88f6ba + languageName: node + linkType: hard + "thunky@npm:^1.0.2": version: 1.1.0 resolution: "thunky@npm:1.1.0" From fc3e99e7946c6796c47f92fe05e5f03b147487b5 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Jan 2025 17:18:34 +0100 Subject: [PATCH 45/53] Update and add backup my links (#23556) * Update and add backup my links --- src/panels/my/ha-panel-my.ts | 30 ++++++++++++++++++------------ src/state/quick-bar-mixin.ts | 4 +--- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/panels/my/ha-panel-my.ts b/src/panels/my/ha-panel-my.ts index cab1cfbafb..c2ae6a8e72 100644 --- a/src/panels/my/ha-panel-my.ts +++ b/src/panels/my/ha-panel-my.ts @@ -16,7 +16,7 @@ import "../../layouts/hass-error-screen"; import type { HomeAssistant, Route } from "../../types"; import { documentationUrl } from "../../util/documentation-url"; -export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({ +export const getMyRedirects = (): Redirects => ({ application_credentials: { redirect: "/config/application_credentials", }, @@ -244,16 +244,24 @@ export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({ redirect: "/media-browser", }, backup: { - component: hasSupervisor ? "hassio" : "backup", - redirect: hasSupervisor ? "/hassio/backups" : "/config/backup", + component: "backup", + redirect: "/config/backup", + }, + backup_list: { + component: "backup", + redirect: "/config/backup/backups", + }, + backup_config: { + component: "backup", + redirect: "/config/backup/settings", }, supervisor_snapshots: { - component: hasSupervisor ? "hassio" : "backup", - redirect: hasSupervisor ? "/hassio/backups" : "/config/backup", + component: "backup", + redirect: "/config/backup", }, supervisor_backups: { - component: hasSupervisor ? "hassio" : "backup", - redirect: hasSupervisor ? "/hassio/backups" : "/config/backup", + component: "backup", + redirect: "/config/backup", }, supervisor_system: { // Moved from Supervisor panel in 2022.5 @@ -278,10 +286,8 @@ export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({ }, }); -const getRedirect = ( - path: string, - hasSupervisor: boolean -): Redirect | undefined => getMyRedirects(hasSupervisor)?.[path]; +const getRedirect = (path: string): Redirect | undefined => + getMyRedirects()?.[path]; export type ParamType = "url" | "string" | "string?"; @@ -314,7 +320,7 @@ class HaPanelMy extends LitElement { const path = this.route.path.substring(1); const hasSupervisor = isComponentLoaded(this.hass, "hassio"); - this._redirect = getRedirect(path, hasSupervisor); + this._redirect = getRedirect(path); if (path.startsWith("supervisor") && this._redirect === undefined) { if (!hasSupervisor) { diff --git a/src/state/quick-bar-mixin.ts b/src/state/quick-bar-mixin.ts index 30a96e7e93..daea444079 100644 --- a/src/state/quick-bar-mixin.ts +++ b/src/state/quick-bar-mixin.ts @@ -150,9 +150,7 @@ export default >(superClass: T) => const myPanel = await import("../panels/my/ha-panel-my"); - for (const [slug, redirect] of Object.entries( - myPanel.getMyRedirects(isHassio) - )) { + for (const [slug, redirect] of Object.entries(myPanel.getMyRedirects())) { if (targetPath.startsWith(redirect.redirect)) { myParams.append("redirect", slug); if (redirect.params) { From 102a9eeb619645375edf741fa0e4db51c6f4ecba Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 11:08:00 +0100 Subject: [PATCH 46/53] Fix tabs subpage height on desktop (#23564) fix tabs subpage height on desktop --- src/layouts/hass-tabs-subpage.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/layouts/hass-tabs-subpage.ts b/src/layouts/hass-tabs-subpage.ts index 4c4d1e3cdc..ca930a01b5 100644 --- a/src/layouts/hass-tabs-subpage.ts +++ b/src/layouts/hass-tabs-subpage.ts @@ -314,10 +314,6 @@ class HassTabsSubpage extends LitElement { width: calc( 100% - env(safe-area-inset-left) - env(safe-area-inset-right) ); - height: calc(100% - var(--header-height)); - height: calc( - 100% - var(--header-height) - env(safe-area-inset-bottom) - ); margin-left: env(safe-area-inset-left); margin-right: env(safe-area-inset-right); margin-inline-start: env(safe-area-inset-left); @@ -326,6 +322,13 @@ class HassTabsSubpage extends LitElement { -webkit-overflow-scrolling: touch; } + :host([narrow]) .content { + height: calc(100% - var(--header-height)); + height: calc( + 100% - var(--header-height) - env(safe-area-inset-bottom) + ); + } + :host([narrow]) .content.tabs { height: calc(100% - 2 * var(--header-height)); height: calc( From d126e02747438e638099871c3295747cf06829e6 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 11:17:27 +0100 Subject: [PATCH 47/53] fix error display upload backup (#23565) --- .../config/backup/dialogs/dialog-upload-backup.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/panels/config/backup/dialogs/dialog-upload-backup.ts b/src/panels/config/backup/dialogs/dialog-upload-backup.ts index ff3e454f22..e1258a0f51 100644 --- a/src/panels/config/backup/dialogs/dialog-upload-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-upload-backup.ts @@ -89,6 +89,9 @@ export class DialogUploadBackup Upload backup
+ ${this._error + ? html`${this._error}` + : nothing} - ${this._error - ? html`${this._error}` - : nothing}
Cancel @@ -160,6 +160,10 @@ export class DialogUploadBackup max-width: 500px; max-height: 100%; } + ha-alert { + display: block; + margin-bottom: 16px; + } `, ]; } From 4ea0c83fbe358e6ab80f9a285b48b9572cdf8f95 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 11:17:40 +0100 Subject: [PATCH 48/53] Close restore dialog if done (#23566) --- src/panels/config/backup/dialogs/dialog-restore-backup.ts | 3 +++ src/panels/config/backup/ha-config-backup.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/panels/config/backup/dialogs/dialog-restore-backup.ts b/src/panels/config/backup/dialogs/dialog-restore-backup.ts index bcc6ecc7bf..73555630ac 100644 --- a/src/panels/config/backup/dialogs/dialog-restore-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-restore-backup.ts @@ -212,6 +212,9 @@ class DialogRestoreBackup extends LitElement implements HassDialog { private _subscribeBackupEvents() { this._unsub = subscribeBackupEvents(this.hass!, (event) => { + if (!this._error && event.manager_state === "idle") { + this.closeDialog(); + } if (event.manager_state !== "restore_backup") { return; } diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index bf8be4d454..7f4a761a48 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -33,7 +33,7 @@ declare global { class HaConfigBackup extends SubscribeMixin(HassRouterPage) { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public cloudStatus!: CloudStatus; + @property({ attribute: false }) public cloudStatus?: CloudStatus; @property({ type: Boolean }) public narrow = false; From 0bf64ee7f4b9e204d32e73518628f95dea2e0b4f Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 11:29:03 +0100 Subject: [PATCH 49/53] Backup onboarding: Show close button when welcome is skipped (#23567) --- .../config/backup/dialogs/dialog-backup-onboarding.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts index 01f881de58..ab66157e46 100644 --- a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts +++ b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts @@ -90,7 +90,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { public showDialog(params: BackupOnboardingDialogParams): void { this._params = params; - this._step = params.skipWelcome ? STEPS[1] : STEPS[0]; + this._step = this._firstStep; this._config = RECOMMENDED_CONFIG; const agents: string[] = []; @@ -129,6 +129,10 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { this._params = undefined; } + private get _firstStep(): Step { + return this._params?.skipWelcome ? STEPS[1] : STEPS[0]; + } + private async _done() { if (!this._config) { return; @@ -187,7 +191,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog { } const isLastStep = this._step === STEPS[STEPS.length - 1]; - const isFirstStep = this._step === STEPS[0]; + const isFirstStep = this._step === this._firstStep; return html` From 70541ec966fcc16e57b777262e625fdb3f282635 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 15:02:59 +0100 Subject: [PATCH 50/53] Fix restore progress check logic (#23568) --- .../backup/dialogs/dialog-restore-backup.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/panels/config/backup/dialogs/dialog-restore-backup.ts b/src/panels/config/backup/dialogs/dialog-restore-backup.ts index 73555630ac..7fc8d26426 100644 --- a/src/panels/config/backup/dialogs/dialog-restore-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-restore-backup.ts @@ -23,7 +23,10 @@ import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; import { haStyle, haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; import type { RestoreBackupDialogParams } from "./show-dialog-restore-backup"; -import type { RestoreBackupStage } from "../../../../data/backup_manager"; +import type { + RestoreBackupStage, + RestoreBackupState, +} from "../../../../data/backup_manager"; import { subscribeBackupEvents } from "../../../../data/backup_manager"; type FormData = { @@ -54,6 +57,8 @@ class DialogRestoreBackup extends LitElement implements HassDialog { @state() private _error?: string; + @state() private _state?: RestoreBackupState; + @state() private _stage?: RestoreBackupStage | null; @state() private _unsub?: Promise; @@ -64,6 +69,10 @@ class DialogRestoreBackup extends LitElement implements HassDialog { this._params = params; this._formData = INITIAL_DATA; + this._userPassword = undefined; + this._error = undefined; + this._state = undefined; + this._stage = undefined; if (this._params.backup.protected) { this._backupEncryptionKey = await this._fetchEncryptionKey(); if (!this._backupEncryptionKey) { @@ -86,6 +95,7 @@ class DialogRestoreBackup extends LitElement implements HassDialog { this._backupEncryptionKey = undefined; this._userPassword = undefined; this._error = undefined; + this._state = undefined; this._stage = undefined; this._step = undefined; this._unsubscribe(); @@ -188,7 +198,6 @@ class DialogRestoreBackup extends LitElement implements HassDialog { this._unsubscribe(); try { this._step = "progress"; - window.addEventListener("connection-status", this._connectionStatus); this._subscribeBackupEvents(); await this._doRestoreBackup( this._userPassword || this._backupEncryptionKey @@ -204,20 +213,15 @@ class DialogRestoreBackup extends LitElement implements HassDialog { } } - private _connectionStatus = (ev) => { - if (ev.detail === "connected") { - this.closeDialog(); - } - }; - private _subscribeBackupEvents() { this._unsub = subscribeBackupEvents(this.hass!, (event) => { - if (!this._error && event.manager_state === "idle") { + if (event.manager_state === "idle" && this._state === "in_progress") { this.closeDialog(); } if (event.manager_state !== "restore_backup") { return; } + this._state = event.state; if (event.state === "completed") { this.closeDialog(); } @@ -231,7 +235,6 @@ class DialogRestoreBackup extends LitElement implements HassDialog { } private _unsubscribe() { - window.removeEventListener("connection-status", this._connectionStatus); if (this._unsub) { const prom = this._unsub.then((unsub) => unsub()); this._unsub = undefined; From e2ad94469aea56a29f6683c5cef2264cedb250ee Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 15:26:50 +0100 Subject: [PATCH 51/53] Fix restoring backup during onboarding (#23569) --- hassio/src/dialogs/backup/dialog-hassio-backup.ts | 10 +++++----- src/data/hassio/backup.ts | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hassio/src/dialogs/backup/dialog-hassio-backup.ts b/hassio/src/dialogs/backup/dialog-hassio-backup.ts index 91b6643094..60e7cc2534 100644 --- a/hassio/src/dialogs/backup/dialog-hassio-backup.ts +++ b/hassio/src/dialogs/backup/dialog-hassio-backup.ts @@ -179,8 +179,8 @@ class HassioBackupDialog } private async _restoreClicked() { - this._restoringBackup = true; const backupDetails = this._backupContent.backupDetails(); + this._restoringBackup = true; const supervisor = this._dialogParams?.supervisor; if (supervisor !== undefined && supervisor.info.state !== "running") { @@ -196,12 +196,12 @@ class HassioBackupDialog if ( !(await showConfirmationDialog(this, { title: this._localize( - this._backupContent.backupType === "full" + this._backup!.type === "full" ? "confirm_restore_full_backup_title" : "confirm_restore_partial_backup_title" ), text: this._localize( - this._backupContent.backupType === "full" + this._backup!.type === "full" ? "confirm_restore_full_backup_text" : "confirm_restore_partial_backup_text" ), @@ -216,9 +216,9 @@ class HassioBackupDialog try { await restoreBackup( this.hass, - this._backupContent.backupType, + this._backup!.type, this._backup!.slug, - backupDetails, + { ...backupDetails, background: this._dialogParams?.onboarding }, !!this.hass && atLeastVersion(this.hass.config.version, 2021, 9) ); diff --git a/src/data/hassio/backup.ts b/src/data/hassio/backup.ts index 8a88e1a5b9..0257128bf7 100644 --- a/src/data/hassio/backup.ts +++ b/src/data/hassio/backup.ts @@ -46,6 +46,7 @@ export interface HassioFullBackupCreateParams { name: string; password?: string; confirm_password?: string; + background?: boolean; } export interface HassioPartialBackupCreateParams extends HassioFullBackupCreateParams { From ec1fc0914093c95255d94390a571ba7e3738d2bf Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 15:42:39 +0100 Subject: [PATCH 52/53] Bumped version to 20250103.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bfbc1ef922..43d693b7b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20250102.0" +version = "20250103.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 47308e7b461c5758d1749a4e59831c059c8b2f34 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 3 Jan 2025 15:48:20 +0100 Subject: [PATCH 53/53] Add change of encryption key warning (#23570) --- .../backup/dialogs/dialog-restore-backup.ts | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/panels/config/backup/dialogs/dialog-restore-backup.ts b/src/panels/config/backup/dialogs/dialog-restore-backup.ts index 7fc8d26426..ecf6a6f156 100644 --- a/src/panels/config/backup/dialogs/dialog-restore-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-restore-backup.ts @@ -55,6 +55,8 @@ class DialogRestoreBackup extends LitElement implements HassDialog { @state() private _userPassword?: string; + @state() private _usedUserInput = false; + @state() private _error?: string; @state() private _state?: RestoreBackupState; @@ -70,6 +72,7 @@ class DialogRestoreBackup extends LitElement implements HassDialog { this._formData = INITIAL_DATA; this._userPassword = undefined; + this._usedUserInput = false; this._error = undefined; this._state = undefined; this._stage = undefined; @@ -94,6 +97,7 @@ class DialogRestoreBackup extends LitElement implements HassDialog { this._params = undefined; this._backupEncryptionKey = undefined; this._userPassword = undefined; + this._usedUserInput = false; this._error = undefined; this._state = undefined; this._stage = undefined; @@ -159,15 +163,24 @@ class DialogRestoreBackup extends LitElement implements HassDialog { } private _renderEncryption() { - return html`

- ${this._userPassword - ? "The provided encryption key was incorrect, please try again." - : this._backupEncryptionKey - ? "The backup is encrypted with a different key or password than that is saved on this system. Please enter the key for this backup." - : "The backup is encrypted. Provide the encryption key to decrypt the backup."} -

+ return html`${this._usedUserInput + ? "The provided encryption key was incorrect, please try again." + : this._backupEncryptionKey + ? html`The Backup is encrypted with a different encryption key than + that is saved on this system. Please enter the encryption key for + this backup.
+ ${this._params!.selectedData.homeassistant_included + ? html`After restoring the backup, your new backups will be + encrypted with the encryption key that was present during + the time of this backup.` + : nothing}` + : "The backup is encrypted. Provide the encryption key to decrypt the backup."} + `; } @@ -196,6 +209,9 @@ class DialogRestoreBackup extends LitElement implements HassDialog { private async _restoreBackup() { this._unsubscribe(); + this._state = undefined; + this._stage = undefined; + this._error = undefined; try { this._step = "progress"; this._subscribeBackupEvents(); @@ -206,6 +222,9 @@ class DialogRestoreBackup extends LitElement implements HassDialog { await this._unsubscribe(); if (e.code === "password_incorrect") { this._error = undefined; + if (this._userPassword) { + this._usedUserInput = true; + } this._step = "encryption"; } else { this._error = e.message; @@ -315,6 +334,14 @@ class DialogRestoreBackup extends LitElement implements HassDialog { ha-circular-progress { margin-bottom: 16px; } + ha-alert[alert-type="warning"] { + display: block; + margin-top: 16px; + } + ha-password-field { + display: block; + margin-top: 16px; + } `, ]; }