From f2ca65c53009be7f8a1ff2c11d2da6236470b4c7 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 22 Jan 2025 18:33:58 +0100 Subject: [PATCH 01/17] Add backup location settings page --- .../config/ha-backup-config-agents.ts | 19 +- .../backup/ha-config-backup-location.ts | 198 ++++++++++++++++++ .../backup/ha-config-backup-settings.ts | 1 + src/panels/config/backup/ha-config-backup.ts | 18 +- src/translations/en.json | 9 + 5 files changed, 239 insertions(+), 6 deletions(-) create mode 100644 src/panels/config/backup/ha-config-backup-location.ts 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 fa682f622439..bccfdb771303 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 @@ -1,9 +1,10 @@ -import { mdiHarddisk, mdiNas } from "@mdi/js"; +import { mdiCogOutline, mdiHarddisk, mdiNas } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../../../../common/dom/fire_event"; import { computeDomain } from "../../../../../common/entity/compute_domain"; +import "../../../../../components/ha-icon-button"; import "../../../../../components/ha-md-list"; import "../../../../../components/ha-md-list-item"; import "../../../../../components/ha-svg-icon"; @@ -18,6 +19,7 @@ import { import type { CloudStatus } from "../../../../../data/cloud"; import type { HomeAssistant } from "../../../../../types"; import { brandsUrl } from "../../../../../util/brands-url"; +import { navigate } from "../../../../../common/navigate"; const DEFAULT_AGENTS = []; @@ -29,6 +31,9 @@ class HaBackupConfigAgents extends LitElement { @property({ attribute: false }) public agents: BackupAgent[] = []; + @property({ type: Boolean, attribute: "show-settings" }) public showSettings = + false; + @state() private value?: string[]; private _availableAgents = memoizeOne( @@ -112,6 +117,13 @@ class HaBackupConfigAgents extends LitElement { ${description ? html`
${description}
` : nothing} + + +
+ ${this._error && + html`${this._error}`} + ${this._agent === null + ? html` + + ${this.hass.localize( + "ui.panel.config.backup.location.not_found_description", + { agentId: this.agentId } + )} + + ` + : !this.agentId + ? html`` + : html` + +
+ ${this.hass.localize( + "ui.panel.config.backup.location.encryption.title" + )} +
+
+ `} +
+ + `; + } + + private async _fetchAgent() { + try { + // Todo fetch agent details + const { agents } = await fetchBackupAgentsInfo(this.hass); + const agent = agents.find((a) => a.agent_id === this.agentId); + if (!agent) { + throw new Error("Agent not found"); + } + this._agent = agent; + this._agentIds = agents.map((a) => a.agent_id); + } catch (err: any) { + this._error = + err?.message || + this.hass.localize("ui.panel.config.backup.details.error"); + } + } + + static styles = css` + .content { + padding: 28px 20px 0; + max-width: 690px; + margin: 0 auto; + gap: 24px; + display: grid; + margin-bottom: 24px; + } + .card-content { + padding: 0 20px; + } + .card-actions { + display: flex; + justify-content: flex-end; + } + ha-md-list { + background: none; + padding: 0; + } + ha-md-list-item { + --md-list-item-leading-space: 0; + --md-list-item-trailing-space: 0; + --md-list-item-two-line-container-height: 64px; + } + ha-md-list-item img { + width: 48px; + } + ha-md-list-item ha-svg-icon[slot="start"] { + --mdc-icon-size: 48px; + color: var(--primary-text-color); + } + ha-md-list.summary ha-md-list-item { + --md-list-item-supporting-text-size: 1rem; + --md-list-item-label-text-size: 0.875rem; + + --md-list-item-label-text-color: var(--secondary-text-color); + --md-list-item-supporting-text-color: var(--primary-text-color); + } + .warning { + color: var(--error-color); + } + .warning ha-svg-icon { + color: var(--error-color); + } + ha-button.danger { + --mdc-theme-primary: var(--error-color); + } + ha-backup-data-picker { + display: block; + } + ha-md-list-item [slot="supporting-text"] { + display: flex; + align-items: center; + flex-direction: row; + gap: 8px; + line-height: normal; + } + .dot { + display: block; + position: relative; + width: 8px; + height: 8px; + background-color: var(--disabled-color); + border-radius: 50%; + flex: none; + } + .dot.success { + background-color: var(--success-color); + } + .dot.error { + background-color: var(--error-color); + } + .card-header { + padding-bottom: 8px; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-backup-location": HaConfigBackupDetails; + } +} diff --git a/src/panels/config/backup/ha-config-backup-settings.ts b/src/panels/config/backup/ha-config-backup-settings.ts index 31bba002a318..007e3a41396e 100644 --- a/src/panels/config/backup/ha-config-backup-settings.ts +++ b/src/panels/config/backup/ha-config-backup-settings.ts @@ -182,6 +182,7 @@ class HaConfigBackupSettings extends LitElement { .cloudStatus=${this.cloudStatus} .agents=${this.agents} @value-changed=${this._agentsConfigChanged} + show-settings > ${!this._config.create_backup.agent_ids.length ? html` diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index 07cd5ff63956..d6d2e086366b 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -124,6 +124,10 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { tag: "ha-config-backup-settings", load: () => import("./ha-config-backup-settings"), }, + location: { + tag: "ha-config-backup-location", + load: () => import("./ha-config-backup-location"), + }, }, }; @@ -138,11 +142,15 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { pageEl.agents = this._agents; pageEl.fetching = this._fetching; - if ( - (!changedProps || changedProps.has("route")) && - this._currentPage === "details" - ) { - pageEl.backupId = this.routeTail.path.substr(1); + if (!changedProps || changedProps.has("route")) { + switch (this._currentPage) { + case "details": + pageEl.backupId = this.routeTail.path.substr(1); + break; + case "location": + pageEl.agentId = this.routeTail.path.substr(1); + break; + } } } diff --git a/src/translations/en.json b/src/translations/en.json index 8762bdad0cf6..2a24d083e94e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2665,6 +2665,15 @@ "backup_failed": "Backup failed", "download": "Download from this location" } + }, + "location": { + "header": "Location", + "not_found": "Not found", + "not_found_description": "Location matching ''{backupId}'' not found", + "error": "Could not fetch location details", + "encryption": { + "title": "Encryption" + } } }, "tag": { From 943fa5419c6279166a6eb5ec576d1536d6feeadb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 23 Jan 2025 16:57:09 +0100 Subject: [PATCH 02/17] Add encryption settings --- .../backup/ha-config-backup-location.ts | 93 +++++++++++++++++++ src/translations/en.json | 11 ++- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/panels/config/backup/ha-config-backup-location.ts b/src/panels/config/backup/ha-config-backup-location.ts index 9968f0bb89ea..1c92b69bfd57 100644 --- a/src/panels/config/backup/ha-config-backup-location.ts +++ b/src/panels/config/backup/ha-config-backup-location.ts @@ -2,6 +2,7 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-alert"; import "../../../components/ha-button"; +import "../../../components/ha-switch"; import "../../../components/ha-button-menu"; import "../../../components/ha-card"; import "../../../components/ha-circular-progress"; @@ -11,12 +12,14 @@ import "../../../components/ha-md-list"; import "../../../components/ha-md-list-item"; import type { BackupAgent, BackupConfig } from "../../../data/backup"; import { + CLOUD_AGENT, computeBackupAgentName, fetchBackupAgentsInfo, } from "../../../data/backup"; import "../../../layouts/hass-subpage"; import type { HomeAssistant } from "../../../types"; import "./components/ha-backup-data-picker"; +import { showConfirmationDialog } from "../../lovelace/custom-card-helpers"; @customElement("ha-config-backup-location") class HaConfigBackupDetails extends LitElement { @@ -34,6 +37,9 @@ class HaConfigBackupDetails extends LitElement { @state() private _error?: string; + // Todo update with api call + @state() private _encrypted = true; + protected firstUpdated(changedProps) { super.firstUpdated(changedProps); @@ -49,6 +55,8 @@ class HaConfigBackupDetails extends LitElement { return nothing; } + const encrypted = this._encrypted; + return html` +
+ + ${CLOUD_AGENT === this.agentId + ? html` + + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption" + )} + + + ${this.hass.localize( + `ui.panel.config.backup.location.encryption.cloud_forced_encryption` + )} + + + + ` + : html` + + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption" + )} + + + ${this.hass.localize( + `ui.panel.config.backup.location.encryption.encryption_description_${encrypted ? "on" : "off"}` + )} + + ${encrypted + ? html` + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption_turn_off" + )} + + ` + : html` + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption_turn_on" + )} + + `} + + `} + +
`} @@ -112,6 +180,31 @@ class HaConfigBackupDetails extends LitElement { } } + private _turnOnEncryption() { + this._encrypted = true; + // Todo call api + } + + private async _turnOffEncryption() { + const response = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption_turn_off_confirm_title" + ), + text: this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption_turn_off_confirm_text" + ), + confirmText: this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption_turn_off_confirm_action" + ), + dismissText: this.hass.localize("ui.common.cancel"), + destructive: true, + }); + if (response) { + this._encrypted = false; + // Todo call api + } + } + static styles = css` .content { padding: 28px 20px 0; diff --git a/src/translations/en.json b/src/translations/en.json index 2a24d083e94e..9042c7db6e37 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2672,7 +2672,16 @@ "not_found_description": "Location matching ''{backupId}'' not found", "error": "Could not fetch location details", "encryption": { - "title": "Encryption" + "title": "Encryption", + "encryption": "Location encryption", + "encryption_description_on": "The backups stored in this location are encrypted.", + "encryption_description_off": "The backups stored in this location are unencrypted.", + "encryption_turn_on": "Turn on", + "encryption_turn_off": "Turn off", + "encryption_turn_off_confirm_title": "Turn encryption off?", + "encryption_turn_off_confirm_text": "All your next backups will not be encrypted on this system and network storager. Please keep your backups private and secure.", + "encryption_turn_off_confirm_action": "Turn encryption off", + "cloud_forced_encryption": "This backups are forced to be encrypted by Home Assistant Cloud." } } }, From 09919da2164f2aae16ae043aecca4da3d4828d98 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 27 Jan 2025 15:14:18 +0100 Subject: [PATCH 03/17] Display unencrypted locations and backups --- src/data/backup.ts | 20 +- .../config/ha-backup-config-agents.ts | 41 +++- .../overview/ha-backup-overview-backups.ts | 7 +- .../config/backup/ha-config-backup-backups.ts | 6 + .../config/backup/ha-config-backup-details.ts | 196 ++++++++++-------- .../backup/ha-config-backup-settings.ts | 1 + src/translations/en.json | 8 +- 7 files changed, 176 insertions(+), 103 deletions(-) diff --git a/src/data/backup.ts b/src/data/backup.ts index e8bb18b7f338..6bf43ade24c8 100644 --- a/src/data/backup.ts +++ b/src/data/backup.ts @@ -57,6 +57,7 @@ export interface BackupConfig { time?: string | null; days: BackupDay[]; }; + agents: BackupAgentsConfig; } export interface BackupMutableConfig { @@ -78,6 +79,13 @@ export interface BackupMutableConfig { time?: string | null; days?: BackupDay[] | null; }; + agents?: BackupAgentsConfig; +} + +export type BackupAgentsConfig = Record; + +export interface BackupAgentConfig { + protected: boolean; } export interface BackupAgent { @@ -85,13 +93,16 @@ export interface BackupAgent { name: string; } +export interface BackupContentAgent { + size: number; + protected: boolean; +} + export interface BackupContent { backup_id: string; date: string; name: string; - protected: boolean; - size: number; - agent_ids?: string[]; + agents: Record; failed_agent_ids?: string[]; with_automatic_settings: boolean; } @@ -305,6 +316,9 @@ export const computeBackupAgentName = ( return showName ? `${domainName}: ${name}` : domainName; }; +export const computeBackupSize = (backup: BackupContent) => + Math.max(...Object.values(backup.agents).map((agent) => agent.size)); + export const compareAgents = (a: string, b: string) => { const isLocalA = isLocalAgent(a); const isLocalB = isLocalAgent(b); 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 bccfdb771303..1a2e656c5ec1 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 @@ -9,7 +9,10 @@ import "../../../../../components/ha-md-list"; import "../../../../../components/ha-md-list-item"; import "../../../../../components/ha-svg-icon"; import "../../../../../components/ha-switch"; -import type { BackupAgent } from "../../../../../data/backup"; +import type { + BackupAgent, + BackupAgentsConfig, +} from "../../../../../data/backup"; import { CLOUD_AGENT, computeBackupAgentName, @@ -34,6 +37,8 @@ class HaBackupConfigAgents extends LitElement { @property({ type: Boolean, attribute: "show-settings" }) public showSettings = false; + @property({ type: Array }) public config?: BackupAgentsConfig; + @state() private value?: string[]; private _availableAgents = memoizeOne( @@ -58,6 +63,20 @@ class HaBackupConfigAgents extends LitElement { "ui.panel.config.backup.agents.cloud_agent_description" ); } + + const encryptionTurnedOff = this.config?.[agentId]?.protected === false; + + if (encryptionTurnedOff) { + return html` + + + ${this.hass.localize( + "ui.panel.config.backup.agents.encryption_turned_off" + )} + + `; + } + if (isNetworkMountAgent(agentId)) { return this.hass.localize( "ui.panel.config.backup.agents.network_mount_agent_description" @@ -85,6 +104,7 @@ class HaBackupConfigAgents extends LitElement { agentId === CLOUD_AGENT && this.cloudStatus.logged_in && !this.cloudStatus.active_subscription; + return html` ${isLocalAgent(agentId) @@ -197,6 +217,25 @@ class HaBackupConfigAgents extends LitElement { --mdc-icon-size: 48px; color: var(--primary-text-color); } + ha-md-list-item [slot="supporting-text"] { + display: flex; + align-items: center; + flex-direction: row; + gap: 8px; + line-height: normal; + } + .dot { + display: block; + position: relative; + width: 8px; + height: 8px; + background-color: var(--disabled-color); + border-radius: 50%; + flex: none; + } + .dot.warning { + background-color: var(--warning-color); + } `; } diff --git a/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts b/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts index 550d34cae65c..882902b60233 100644 --- a/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts +++ b/src/panels/config/backup/components/overview/ha-backup-overview-backups.ts @@ -8,7 +8,10 @@ import "../../../../../components/ha-card"; import "../../../../../components/ha-icon-next"; import "../../../../../components/ha-md-list"; import "../../../../../components/ha-md-list-item"; -import type { BackupContent } from "../../../../../data/backup"; +import { + computeBackupSize, + type BackupContent, +} from "../../../../../data/backup"; import { haStyle } from "../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../types"; import { bytesToString } from "../../../../../util/bytes-to-string"; @@ -22,7 +25,7 @@ const computeBackupStats = (backups: BackupContent[]): BackupStats => backups.reduce( (stats, backup) => { stats.count++; - stats.size += backup.size; + stats.size += computeBackupSize(backup); return stats; }, { count: 0, size: 0 } diff --git a/src/panels/config/backup/ha-config-backup-backups.ts b/src/panels/config/backup/ha-config-backup-backups.ts index 4ac0dfca819a..315403bbede1 100644 --- a/src/panels/config/backup/ha-config-backup-backups.ts +++ b/src/panels/config/backup/ha-config-backup-backups.ts @@ -39,7 +39,9 @@ import type { BackupContent, } from "../../../data/backup"; import { + compareAgents, computeBackupAgentName, + computeBackupSize, deleteBackup, generateBackup, generateBackupWithAutomaticSettings, @@ -68,6 +70,8 @@ import { downloadBackup } from "./helper/download_backup"; interface BackupRow extends DataTableRowData, BackupContent { formatted_type: string; + size: number; + agent_ids: string[]; } type BackupType = "automatic" | "manual"; @@ -291,6 +295,8 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) { const type = backup.with_automatic_settings ? "automatic" : "manual"; return { ...backup, + size: computeBackupSize(backup), + agent_ids: Object.keys(backup.agents).sort(compareAgents), formatted_type: localize(`ui.panel.config.backup.type.${type}`), }; }); diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index 9a80de3bf274..83e37a55b2f7 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -29,6 +29,7 @@ import type { import { compareAgents, computeBackupAgentName, + computeBackupSize, deleteBackup, fetchBackupDetails, isLocalAgent, @@ -47,19 +48,26 @@ import { downloadBackup } from "./helper/download_backup"; interface Agent { id: string; + protected: boolean; + size: number; success: boolean; } -const computeAgents = (agent_ids: string[], failed_agent_ids: string[]) => - [ - ...agent_ids.filter((id) => !failed_agent_ids.includes(id)), - ...failed_agent_ids, +const computeAgents = (backup: BackupContentExtended) => { + const agentIds = Object.keys(backup.agents); + const failedAgentIds = backup.failed_agent_ids || []; + return [ + ...agentIds.filter((id) => !failedAgentIds.includes(id)), + ...failedAgentIds, ] .map((id) => ({ id, - success: !failed_agent_ids.includes(id), + success: !failedAgentIds.includes(id), + protected: backup.agents[id].protected, + size: backup.agents[id].size, })) .sort((a, b) => compareAgents(a.id, b.id)); +}; @customElement("ha-config-backup-details") class HaConfigBackupDetails extends LitElement { @@ -156,7 +164,7 @@ class HaConfigBackupDetails extends LitElement { )} - ${bytesToString(this._backup.size)} + ${bytesToString(computeBackupSize(this._backup))} @@ -173,22 +181,6 @@ class HaConfigBackupDetails extends LitElement { )} - - - ${this.hass.localize( - "ui.panel.config.backup.details.summary.protection" - )} - - - ${this._backup.protected - ? this.hass.localize( - "ui.panel.config.backup.details.summary.protected_encrypted_aes_128" - ) - : this.hass.localize( - "ui.panel.config.backup.details.summary.protected_not_encrypted" - )} - - @@ -230,87 +222,107 @@ class HaConfigBackupDetails extends LitElement { ${this._agents.map((agent) => { const agentId = agent.id; - const success = agent.success; + const domain = computeDomain(agentId); const name = computeBackupAgentName( this.hass.localize, agentId, this.agents ); + const failed = !agent.success; + const unencrypted = agent.protected; + const success = agent.success; return html` - ${isLocalAgent(agentId) - ? html` - - - ` - : isNetworkMountAgent(agentId) + ${ + isLocalAgent(agentId) ? html` + > + ` - : html` - - `} + : isNetworkMountAgent(agentId) + ? html` + + ` + : html` + + ` + }
${name}
-
- - - - ${success - ? this.hass.localize( - "ui.panel.config.backup.details.locations.backup_stored" - ) - : this.hass.localize( - "ui.panel.config.backup.details.locations.backup_failed" - )} - + ${ + failed + ? html` +
+ + + ${this.hass.localize( + "ui.panel.config.backup.details.locations.backup_failed" + )} + +
+ ` + : unencrypted + ? html` +
+ + + ${this.hass.localize( + "ui.panel.config.backup.details.locations.encryption_turned_off" + )} + +
+ ` + : nothing + }
- ${success - ? html` - - - - ${this.hass.localize( - "ui.panel.config.backup.details.locations.download" - )} - - ` - : nothing} + ${ + success + ? html` + + + + + ${this.hass.localize( + "ui.panel.config.backup.details.locations.download" + )} + + + ` + : nothing + }
`; })} @@ -354,10 +366,7 @@ class HaConfigBackupDetails extends LitElement { try { const response = await fetchBackupDetails(this.hass, this.backupId); this._backup = response.backup; - this._agents = computeAgents( - response.backup.agent_ids || [], - response.backup.failed_agent_ids || [] - ); + this._agents = computeAgents(response.backup); } catch (err: any) { this._error = err?.message || @@ -479,6 +488,9 @@ class HaConfigBackupDetails extends LitElement { .dot.success { background-color: var(--success-color); } + .dot.warning { + background-color: var(--warning-color); + } .dot.error { background-color: var(--error-color); } diff --git a/src/panels/config/backup/ha-config-backup-settings.ts b/src/panels/config/backup/ha-config-backup-settings.ts index 007e3a41396e..80c12475a205 100644 --- a/src/panels/config/backup/ha-config-backup-settings.ts +++ b/src/panels/config/backup/ha-config-backup-settings.ts @@ -179,6 +179,7 @@ class HaConfigBackupSettings extends LitElement { Date: Mon, 27 Jan 2025 15:52:42 +0100 Subject: [PATCH 04/17] Improve cloud detail page --- .../config/ha-backup-config-agents.ts | 4 +- .../backup/ha-config-backup-location.ts | 170 ++++++++++++------ src/translations/en.json | 19 +- 3 files changed, 134 insertions(+), 59 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 1a2e656c5ec1..35db26e67f4c 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 @@ -1,4 +1,4 @@ -import { mdiCogOutline, mdiHarddisk, mdiNas } from "@mdi/js"; +import { mdiCog, mdiHarddisk, mdiNas } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -141,7 +141,7 @@ class HaBackupConfigAgents extends LitElement { ` : html` + ${CLOUD_AGENT === this.agentId + ? html` + +
+ ${this.hass.localize( + "ui.panel.config.backup.location.configuration.title" + )} +
+
+

+ ${this.hass.localize( + "ui.panel.config.backup.location.configuration.cloud_description" + )} +

+
+
+ ` + : nothing}
${this.hass.localize( @@ -97,63 +118,87 @@ class HaConfigBackupDetails extends LitElement { )}
+

+ ${this.hass.localize( + "ui.panel.config.backup.location.encryption.description" + )} +

${CLOUD_AGENT === this.agentId ? html` ${this.hass.localize( - "ui.panel.config.backup.location.encryption.encryption" + "ui.panel.config.backup.location.encryption.location_encrypted" )} ${this.hass.localize( - `ui.panel.config.backup.location.encryption.cloud_forced_encryption` + "ui.panel.config.backup.location.encryption.location_encrypted_cloud_description" )} - + rel="noreferrer noopener" + > + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.location_encrypted_cloud_learn_more" + )} + + ` - : html` - - - ${this.hass.localize( - "ui.panel.config.backup.location.encryption.encryption" - )} - - - ${this.hass.localize( - `ui.panel.config.backup.location.encryption.encryption_description_${encrypted ? "on" : "off"}` - )} - - ${encrypted - ? html` - - ${this.hass.localize( - "ui.panel.config.backup.location.encryption.encryption_turn_off" - )} - - ` - : html` - - ${this.hass.localize( - "ui.panel.config.backup.location.encryption.encryption_turn_on" - )} - - `} - - `} + : encrypted + ? html` + + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.location_encrypted" + )} + + + ${this.hass.localize( + `ui.panel.config.backup.location.encryption.location_encrypted_description` + )} + + + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption_turn_off" + )} + + + ` + : html` + + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.location_unencrypted" + )} + + + ${this.hass.localize( + `ui.panel.config.backup.location.encryption.location_unencrypted_description` + )} + + + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.encryption_turn_on" + )} + + + `}
@@ -163,6 +208,17 @@ class HaConfigBackupDetails extends LitElement { `; } + private _isEncryptionTurnedOn() { + const agentConfig = this.config?.agents[this.agentId] as + | BackupAgentConfig + | undefined; + + if (!agentConfig) { + return true; + } + return agentConfig.protected; + } + private async _fetchAgent() { try { // Todo fetch agent details @@ -180,9 +236,22 @@ class HaConfigBackupDetails extends LitElement { } } + private async _updateAgentEncryption(value: boolean) { + const agentsConfig = { + ...this.config?.agents, + [this.agentId]: { + ...this.config?.agents[this.agentId], + protected: value, + }, + }; + await updateBackupConfig(this.hass, { + agents: agentsConfig, + }); + fireEvent(this, "ha-refresh-backup-config"); + } + private _turnOnEncryption() { - this._encrypted = true; - // Todo call api + this._updateAgentEncryption(true); } private async _turnOffEncryption() { @@ -200,8 +269,7 @@ class HaConfigBackupDetails extends LitElement { destructive: true, }); if (response) { - this._encrypted = false; - // Todo call api + this._updateAgentEncryption(false); } } diff --git a/src/translations/en.json b/src/translations/en.json index 765e22fb82ff..676321715f7c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2669,17 +2669,24 @@ "not_found": "Not found", "not_found_description": "Location matching ''{backupId}'' not found", "error": "Could not fetch location details", + "configuration": { + "title": "Configuration", + "cloud_description": "Home Assistant Cloud backup stores only one backup. The oldest backups are deleted." + }, "encryption": { "title": "Encryption", - "encryption": "Location encryption", - "encryption_description_on": "The backups stored in this location are encrypted.", - "encryption_description_off": "The backups stored in this location are unencrypted.", + "description": "All your backups are encrypted by default to keep your data private and secure.", + "location_encrypted": "This location is encrypted", + "location_unencrypted": "This location is unencrypted", + "location_encrypted_description": "Your data private and secure by securing it with your encryption key.", + "location_encrypted_cloud_description": "Home Assistant Cloud is the privacy-focused cloud. This is why it will only accept encrypted backups and why we don’t store your encryption key.", + "location_encrypted_cloud_learn_more": "Learn more", + "location_unencrypted_description": "Please keep your backups private and secure.", "encryption_turn_on": "Turn on", "encryption_turn_off": "Turn off", "encryption_turn_off_confirm_title": "Turn encryption off?", - "encryption_turn_off_confirm_text": "All your next backups will not be encrypted on this system and network storager. Please keep your backups private and secure.", - "encryption_turn_off_confirm_action": "Turn encryption off", - "cloud_forced_encryption": "This backups are forced to be encrypted by Home Assistant Cloud." + "encryption_turn_off_confirm_text": "All your next backups will not be encrypted on This system. Please keep your backups private and secure.", + "encryption_turn_off_confirm_action": "Turn encryption off" } } }, From 72aa5e2c61f34058d4fc98d9beb02fa4c36f77a0 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 27 Jan 2025 15:58:35 +0100 Subject: [PATCH 05/17] Fix encryption flag --- src/panels/config/backup/ha-config-backup-details.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index 83e37a55b2f7..8f0fbc73fb63 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -229,9 +229,9 @@ class HaConfigBackupDetails extends LitElement { agentId, this.agents ); - const failed = !agent.success; - const unencrypted = agent.protected; const success = agent.success; + const failed = !agent.success; + const unencrypted = !agent.protected; return html` From efb296cd8bcbf7d74a7cf437da72f78cf0d9620a Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 27 Jan 2025 17:57:33 +0100 Subject: [PATCH 06/17] Fix restore backup --- .../backup/dialogs/dialog-restore-backup.ts | 12 ++++++++---- .../config/backup/helper/download_backup.ts | 16 ++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/panels/config/backup/dialogs/dialog-restore-backup.ts b/src/panels/config/backup/dialogs/dialog-restore-backup.ts index 7703a7c3e21a..5a7ad2cfd90f 100644 --- a/src/panels/config/backup/dialogs/dialog-restore-backup.ts +++ b/src/panels/config/backup/dialogs/dialog-restore-backup.ts @@ -78,7 +78,12 @@ class DialogRestoreBackup extends LitElement implements HassDialog { this._error = undefined; this._state = undefined; this._stage = undefined; - if (this._params.backup.protected) { + + const agentIds = Object.keys(this._params.backup.agents); + const preferedAgent = getPreferredAgentForDownload(agentIds); + const isProtected = this._params.backup.agents[preferedAgent]?.protected; + + if (isProtected) { this._backupEncryptionKey = await this._fetchEncryptionKey(); if (!this._backupEncryptionKey) { this._step = STEPS[1]; @@ -322,9 +327,8 @@ class DialogRestoreBackup extends LitElement implements HassDialog { return; } - const preferedAgent = getPreferredAgentForDownload( - this._params.backup.agent_ids! - ); + const agentIds = Object.keys(this._params.backup.agents); + const preferedAgent = getPreferredAgentForDownload(agentIds); const { addons, database_included, homeassistant_included, folders } = this._params.selectedData; diff --git a/src/panels/config/backup/helper/download_backup.ts b/src/panels/config/backup/helper/download_backup.ts index 6f8b48258e37..e6b2f8d2c7cc 100644 --- a/src/panels/config/backup/helper/download_backup.ts +++ b/src/panels/config/backup/helper/download_backup.ts @@ -42,11 +42,10 @@ const downloadEncryptedBackup = async ( confirmText: "Download encrypted", }) ) { - triggerDownload( - hass, - backup.backup_id, - agentId ?? getPreferredAgentForDownload(backup.agent_ids!) - ); + const agentIds = Object.keys(backup.agents); + const preferedAgent = agentId ?? getPreferredAgentForDownload(agentIds); + + triggerDownload(hass, backup.backup_id, preferedAgent); } }; @@ -83,10 +82,11 @@ export const downloadBackup = async ( agentId?: string, userProvided = false ): Promise => { - const preferedAgent = - agentId ?? getPreferredAgentForDownload(backup.agent_ids!); + const agentIds = Object.keys(backup.agents); + const preferedAgent = agentId ?? getPreferredAgentForDownload(agentIds); + const isProtected = backup.agents[preferedAgent]?.protected; - if (backup.protected) { + if (isProtected) { if (encryptionKey) { try { await canDecryptBackupOnDownload( From d0a70fdc2a05a33ea7c7fd4a6b884dffbf105f1f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 27 Jan 2025 18:01:57 +0100 Subject: [PATCH 07/17] Fix lint --- .../components/overview/ha-backup-overview-summary.ts | 8 +++++--- .../config/backup/dialogs/dialog-backup-onboarding.ts | 1 + src/panels/config/backup/ha-config-backup.ts | 6 +----- 3 files changed, 7 insertions(+), 8 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 f7c817c7291e..b238ad9767ab 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 @@ -179,7 +179,8 @@ class HaBackupOverviewBackups extends LitElement { now, true ), - count: lastUploadedBackup.agent_ids?.length ?? 0, + count: Object.keys(lastUploadedBackup.agents) + .length, } ) : nextBackupDescription} @@ -265,7 +266,8 @@ class HaBackupOverviewBackups extends LitElement { now, true ), - count: lastUploadedBackup.agent_ids?.length ?? 0, + count: Object.keys(lastUploadedBackup.agents) + .length, } ) : nextBackupDescription} @@ -286,7 +288,7 @@ class HaBackupOverviewBackups extends LitElement { now, true ), - count: lastBackup.agent_ids?.length ?? 0, + count: Object.keys(lastBackup.agents).length, } ); diff --git a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts index f39cd2b270f6..7fa1e09dd87d 100644 --- a/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts +++ b/src/panels/config/backup/dialogs/dialog-backup-onboarding.ts @@ -72,6 +72,7 @@ const RECOMMENDED_CONFIG: BackupConfig = { time: null, days: [], }, + agents: {}, last_attempted_automatic_backup: null, last_completed_automatic_backup: null, next_automatic_backup: null, diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index d6d2e086366b..245927dffe57 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -88,11 +88,7 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { private async _fetchBackupInfo() { const info = await fetchBackupInfo(this.hass); - this._backups = info.backups.map((backup) => ({ - ...backup, - agent_ids: backup.agent_ids?.sort(compareAgents), - failed_agent_ids: backup.failed_agent_ids?.sort(compareAgents), - })); + this._backups = info.backups; } private async _fetchBackupConfig() { From c6b3c31cc53306279e705752a7657b61d037ee1c Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 27 Jan 2025 18:17:33 +0100 Subject: [PATCH 08/17] Fix translations --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 676321715f7c..8c3e4e783d87 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2685,7 +2685,7 @@ "encryption_turn_on": "Turn on", "encryption_turn_off": "Turn off", "encryption_turn_off_confirm_title": "Turn encryption off?", - "encryption_turn_off_confirm_text": "All your next backups will not be encrypted on This system. Please keep your backups private and secure.", + "encryption_turn_off_confirm_text": "All your next backups will not be encrypted for this location. Please keep your backups private and secure.", "encryption_turn_off_confirm_action": "Turn encryption off" } } From 8b4e2b6434ed1716cb34b128d28319526a60e609 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 27 Jan 2025 18:20:32 +0100 Subject: [PATCH 09/17] Add warning --- src/panels/config/backup/ha-config-backup-location.ts | 10 ++++++++++ src/translations/en.json | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/panels/config/backup/ha-config-backup-location.ts b/src/panels/config/backup/ha-config-backup-location.ts index f3ba18b261b9..71c41bd6c367 100644 --- a/src/panels/config/backup/ha-config-backup-location.ts +++ b/src/panels/config/backup/ha-config-backup-location.ts @@ -177,6 +177,16 @@ class HaConfigBackupDetails extends LitElement { ` : html` + + ${this.hass.localize( + "ui.panel.config.backup.location.encryption.warning_encryption_turn_off_description" + )} + ${this.hass.localize( diff --git a/src/translations/en.json b/src/translations/en.json index 8c3e4e783d87..bb4983de91af 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2686,7 +2686,9 @@ "encryption_turn_off": "Turn off", "encryption_turn_off_confirm_title": "Turn encryption off?", "encryption_turn_off_confirm_text": "All your next backups will not be encrypted for this location. Please keep your backups private and secure.", - "encryption_turn_off_confirm_action": "Turn encryption off" + "encryption_turn_off_confirm_action": "Turn encryption off", + "warning_encryption_turn_off": "Encryption turned off", + "warning_encryption_turn_off_description": "All your next backups will not be encrypted." } } }, From a109b24f92bfbd81be1a4d502c742855f5c495a5 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 28 Jan 2025 13:59:57 +0100 Subject: [PATCH 10/17] Use agents in backup locations --- src/panels/config/backup/ha-config-backup-location.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/panels/config/backup/ha-config-backup-location.ts b/src/panels/config/backup/ha-config-backup-location.ts index 71c41bd6c367..076ce37605c4 100644 --- a/src/panels/config/backup/ha-config-backup-location.ts +++ b/src/panels/config/backup/ha-config-backup-location.ts @@ -37,9 +37,9 @@ class HaConfigBackupDetails extends LitElement { @property({ attribute: false }) public config?: BackupConfig; - @state() private _agent?: BackupAgent | null; + @property({ attribute: false }) public agents: BackupAgent[] = []; - @state() private _agentIds?: string[]; + @state() private _agent?: BackupAgent | null; @state() private _error?: string; @@ -69,7 +69,7 @@ class HaConfigBackupDetails extends LitElement { computeBackupAgentName( this.hass.localize, this.agentId, - this._agentIds + this.agents )) || this.hass.localize("ui.panel.config.backup.location.header")} > @@ -238,7 +238,6 @@ class HaConfigBackupDetails extends LitElement { throw new Error("Agent not found"); } this._agent = agent; - this._agentIds = agents.map((a) => a.agent_id); } catch (err: any) { this._error = err?.message || From 9d06af80719cdd43b49ebd1c8de9404c30f95d29 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 28 Jan 2025 15:02:25 +0100 Subject: [PATCH 11/17] Feedback --- .../config/ha-backup-config-agents.ts | 22 +++++++++++-------- .../backup/ha-config-backup-settings.ts | 2 +- 2 files changed, 14 insertions(+), 10 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 35db26e67f4c..444135b1a743 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 @@ -37,7 +37,7 @@ class HaBackupConfigAgents extends LitElement { @property({ type: Boolean, attribute: "show-settings" }) public showSettings = false; - @property({ type: Array }) public config?: BackupAgentsConfig; + @property({ attribute: false }) public agentsConfig?: BackupAgentsConfig; @state() private value?: string[]; @@ -64,7 +64,8 @@ class HaBackupConfigAgents extends LitElement { ); } - const encryptionTurnedOff = this.config?.[agentId]?.protected === false; + const encryptionTurnedOff = + this.agentsConfig?.[agentId]?.protected === false; if (encryptionTurnedOff) { return html` @@ -137,13 +138,16 @@ class HaBackupConfigAgents extends LitElement { ${description ? html`
${description}
` : nothing} - - + ${this.showSettings + ? html` + + ` + : nothing} Date: Tue, 28 Jan 2025 15:33:48 +0100 Subject: [PATCH 12/17] Use updated --- .../config/backup/ha-config-backup-location.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/panels/config/backup/ha-config-backup-location.ts b/src/panels/config/backup/ha-config-backup-location.ts index 076ce37605c4..aaa83e8def45 100644 --- a/src/panels/config/backup/ha-config-backup-location.ts +++ b/src/panels/config/backup/ha-config-backup-location.ts @@ -1,3 +1,4 @@ +import type { PropertyValues } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-alert"; @@ -43,13 +44,13 @@ class HaConfigBackupDetails extends LitElement { @state() private _error?: string; - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - - if (this.agentId) { - this._fetchAgent(); - } else { - this._error = "Agent id not defined"; + protected updated(changedProps: PropertyValues): void { + if (changedProps.has("agentId")) { + if (this.agentId) { + this._fetchAgent(); + } else { + this._error = "Agent id not defined"; + } } } From 6bd30866e964be162945abda9f04ad0f1bc1d1fa Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 28 Jan 2025 16:30:34 +0100 Subject: [PATCH 13/17] Improve encrypted/unencrypted status --- .../config/backup/ha-config-backup-details.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index 8f0fbc73fb63..50ef9227770f 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -55,7 +55,7 @@ interface Agent { const computeAgents = (backup: BackupContentExtended) => { const agentIds = Object.keys(backup.agents); - const failedAgentIds = backup.failed_agent_ids || []; + const failedAgentIds = ["kitchen_sink.syncer"]; return [ ...agentIds.filter((id) => !failedAgentIds.includes(id)), ...failedAgentIds, @@ -63,8 +63,8 @@ const computeAgents = (backup: BackupContentExtended) => { .map((id) => ({ id, success: !failedAgentIds.includes(id), - protected: backup.agents[id].protected, - size: backup.agents[id].size, + protected: backup.agents[id]?.protected ?? false, + size: backup.agents[id]?.size ?? 0, })) .sort((a, b) => compareAgents(a.id, b.id)); }; @@ -284,14 +284,15 @@ class HaConfigBackupDetails extends LitElement { ? html`
- - ${this.hass.localize( - "ui.panel.config.backup.details.locations.encryption_turned_off" - )} - + Unencrypted +
+ ` + : html` +
+ + Encrypted
` - : nothing } ${ From 8f2f305692227d495b9ef81c0c64c0b7695df64e Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 29 Jan 2025 09:23:26 +0100 Subject: [PATCH 14/17] Improve code quality --- .../backup/components/config/ha-backup-config-agents.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 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 444135b1a743..3d6463af8cae 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 @@ -34,11 +34,11 @@ class HaBackupConfigAgents extends LitElement { @property({ attribute: false }) public agents: BackupAgent[] = []; + @property({ attribute: false }) public agentsConfig?: BackupAgentsConfig; + @property({ type: Boolean, attribute: "show-settings" }) public showSettings = false; - @property({ attribute: false }) public agentsConfig?: BackupAgentsConfig; - @state() private value?: string[]; private _availableAgents = memoizeOne( From 11c1d5c226d07d199345c938487779eadb775c46 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 29 Jan 2025 09:27:53 +0100 Subject: [PATCH 15/17] Remove hardcoded failed id --- src/panels/config/backup/ha-config-backup-details.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index 50ef9227770f..2d17794b1a4e 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -55,7 +55,7 @@ interface Agent { const computeAgents = (backup: BackupContentExtended) => { const agentIds = Object.keys(backup.agents); - const failedAgentIds = ["kitchen_sink.syncer"]; + const failedAgentIds = backup.failed_agent_ids ?? []; return [ ...agentIds.filter((id) => !failedAgentIds.includes(id)), ...failedAgentIds, From f8eef6397a41e986d75684bb07c01915f1be9e71 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 29 Jan 2025 09:32:36 +0100 Subject: [PATCH 16/17] Extend agent interface --- .../config/backup/ha-config-backup-details.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/panels/config/backup/ha-config-backup-details.ts b/src/panels/config/backup/ha-config-backup-details.ts index 2d17794b1a4e..ed6c33cd830f 100644 --- a/src/panels/config/backup/ha-config-backup-details.ts +++ b/src/panels/config/backup/ha-config-backup-details.ts @@ -23,6 +23,7 @@ import "../../../components/ha-md-list-item"; import type { BackupAgent, BackupConfig, + BackupContentAgent, BackupContentExtended, BackupData, } from "../../../data/backup"; @@ -46,10 +47,8 @@ import { fireEvent } from "../../../common/dom/fire_event"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { downloadBackup } from "./helper/download_backup"; -interface Agent { +interface Agent extends BackupContentAgent { id: string; - protected: boolean; - size: number; success: boolean; } @@ -60,12 +59,17 @@ const computeAgents = (backup: BackupContentExtended) => { ...agentIds.filter((id) => !failedAgentIds.includes(id)), ...failedAgentIds, ] - .map((id) => ({ - id, - success: !failedAgentIds.includes(id), - protected: backup.agents[id]?.protected ?? false, - size: backup.agents[id]?.size ?? 0, - })) + .map((id) => { + const agent: BackupContentAgent = backup.agents[id] ?? { + protected: false, + size: 0, + }; + return { + ...agent, + id: id, + success: !failedAgentIds.includes(id), + }; + }) .sort((a, b) => compareAgents(a.id, b.id)); }; From d60412d5e69e5cfdedef6edee328e7888be66b37 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 29 Jan 2025 14:10:00 +0100 Subject: [PATCH 17/17] Use willupdate --- src/panels/config/backup/ha-config-backup-location.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/backup/ha-config-backup-location.ts b/src/panels/config/backup/ha-config-backup-location.ts index aaa83e8def45..14118dc33222 100644 --- a/src/panels/config/backup/ha-config-backup-location.ts +++ b/src/panels/config/backup/ha-config-backup-location.ts @@ -44,7 +44,7 @@ class HaConfigBackupDetails extends LitElement { @state() private _error?: string; - protected updated(changedProps: PropertyValues): void { + protected willUpdate(changedProps: PropertyValues): void { if (changedProps.has("agentId")) { if (this.agentId) { this._fetchAgent();