From 04d84c7740b2d363ed79e31026f334721c71d6fa Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 6 Jan 2025 15:42:52 +0100 Subject: [PATCH 1/4] Improve error handling in backup status banner --- .../overview/ha-backup-overview-summary.ts | 98 +++++++++++++------ src/panels/config/backup/ha-config-backup.ts | 24 +++-- 2 files changed, 85 insertions(+), 37 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 e6e4d7819559..37d5c4109cb5 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 @@ -31,14 +31,24 @@ class HaBackupOverviewBackups extends LitElement { @property({ type: Boolean }) public fetching = false; - private _lastSuccessfulBackup = memoizeOne((backups: BackupContent[]) => { - const sortedBackups = backups + private _sortedBackups = memoizeOne((backups: BackupContent[]) => + backups .filter((backup) => backup.with_automatic_settings) - .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) + ); + private _lastCompletedBackup = memoizeOne((backups: BackupContent[]) => { + const sortedBackups = this._sortedBackups(backups); return sortedBackups[0] as BackupContent | undefined; }); + private _lastUploadedBackup = memoizeOne((backups: BackupContent[]) => { + const sortedBackups = this._sortedBackups(backups); + return sortedBackups.find( + (backup) => backup.failed_agent_ids?.length === 0 + ); + }); + private _nextBackupDescription(schedule: BackupScheduleState) { const time = getFormattedBackupTime(this.hass.locale, this.hass.config); @@ -65,6 +75,8 @@ class HaBackupOverviewBackups extends LitElement { } protected render() { + const now = new Date(); + if (this.fetching) { return html` @@ -82,24 +94,44 @@ class HaBackupOverviewBackups extends LitElement { `; } - const lastSuccessfulBackup = this._lastSuccessfulBackup(this.backups); + const lastCompletedBackup = this._lastCompletedBackup(this.backups); - const lastAttempt = this.config.last_attempted_automatic_backup + const nextBackupDescription = this._nextBackupDescription( + this.config.schedule.state + ); + + const lastAttemptDate = this.config.last_attempted_automatic_backup ? new Date(this.config.last_attempted_automatic_backup) - : undefined; + : new Date(0); - const lastCompletedBackupDate = this.config.last_completed_automatic_backup - ? new Date(this.config.last_completed_automatic_backup) - : undefined; + // If no backups yet, show warning + if (!lastCompletedBackup) { + const description = "You have no automatic backups yet."; + return html` + + + + + ${description} + + + + ${nextBackupDescription} + + + + `; + } - const now = new Date(); + const lastCompletedDate = new Date(lastCompletedBackup.date); - const lastBackupDescription = lastSuccessfulBackup - ? `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 last attempt is after last completed backup, show error + if (lastAttemptDate > lastCompletedDate) { + const description = `The last automatic backup triggered ${relativeTime(lastAttemptDate, this.hass.locale, now, true)} wasn't successful.`; - if (lastAttempt && lastAttempt > (lastCompletedBackupDate || 0)) { - const lastAttemptDescription = `The last automatic backup triggered ${relativeTime(lastAttempt, this.hass.locale, now, true)} wasn't successful.`; return html` - ${lastAttemptDescription} + ${description} - ${lastBackupDescription} + ${nextBackupDescription} `; } - const nextBackupDescription = this._nextBackupDescription( - this.config.schedule.state - ); + // If last backup + if (lastCompletedBackup.failed_agent_ids?.length) { + const description = `The last automatic backup created ${relativeTime(lastCompletedDate, this.hass.locale, now, true)} wasn't stored to all locations.`; + + const lastUploadedBackup = this._lastUploadedBackup(this.backups); + const secondaryDescription = lastUploadedBackup + ? `Last successful backup ${relativeTime(new Date(lastUploadedBackup.date), this.hass.locale, now, true)} and stored in ${lastUploadedBackup.agent_ids?.length} locations.` + : nextBackupDescription; - if (!lastSuccessfulBackup) { return html` + + + ${description} + - ${nextBackupDescription} + ${secondaryDescription} `; } + const description = `Last successful backup ${relativeTime(new Date(lastCompletedBackup.date), this.hass.locale, now, true)} and stored in ${lastCompletedBackup.agent_ids?.length} locations.`; + 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), - new Date(lastSuccessfulBackup.date) + new Date(lastCompletedBackup.date) ); const isOverdue = @@ -160,7 +201,7 @@ class HaBackupOverviewBackups extends LitElement { - ${lastBackupDescription} + ${description} @@ -170,12 +211,13 @@ class HaBackupOverviewBackups extends LitElement { `; } + return html` - ${lastBackupDescription} + ${description} diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index 7f4a761a48aa..2737fd99caff 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -1,6 +1,12 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { PropertyValues } from "lit"; import { customElement, property, state } from "lit/decorators"; +import type { BackupConfig, BackupContent } from "../../../data/backup"; +import { + compareAgents, + fetchBackupConfig, + fetchBackupInfo, +} from "../../../data/backup"; import type { ManagerStateEvent } from "../../../data/backup_manager"; import { DEFAULT_MANAGER_STATE, @@ -15,12 +21,6 @@ import type { HomeAssistant } from "../../../types"; import { showToast } from "../../../util/toast"; import "./ha-config-backup-backups"; import "./ha-config-backup-overview"; -import type { BackupConfig, BackupContent } from "../../../data/backup"; -import { - compareAgents, - fetchBackupConfig, - fetchBackupInfo, -} from "../../../data/backup"; declare global { interface HASSDomEvents { @@ -128,11 +128,17 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { public hassSubscribe(): Promise[] { return [ subscribeBackupEvents(this.hass!, (event) => { + const curState = this._manager.manager_state; + this._manager = event; + if ( + event.manager_state === "idle" && + event.manager_state !== curState + ) { + this._fetchBackupConfig(); + this._fetchBackupInfo(); + } if ("state" in event) { - if (event.state === "completed" || event.state === "failed") { - this._fetchBackupInfo(); - } if (event.state === "failed") { let message = ""; switch (this._manager.manager_state) { From 36ce6b47b6617806279a589ef7265d3ecad76931 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 6 Jan 2025 15:52:21 +0100 Subject: [PATCH 2/4] Fix completion --- .../overview/ha-backup-overview-summary.ts | 28 +++++++++++-------- 1 file changed, 16 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 37d5c4109cb5..c4040d3422e5 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 @@ -37,7 +37,7 @@ class HaBackupOverviewBackups extends LitElement { .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) ); - private _lastCompletedBackup = memoizeOne((backups: BackupContent[]) => { + private _lastBackup = memoizeOne((backups: BackupContent[]) => { const sortedBackups = this._sortedBackups(backups); return sortedBackups[0] as BackupContent | undefined; }); @@ -94,18 +94,14 @@ class HaBackupOverviewBackups extends LitElement { `; } - const lastCompletedBackup = this._lastCompletedBackup(this.backups); + const lastBackup = this._lastBackup(this.backups); const nextBackupDescription = this._nextBackupDescription( this.config.schedule.state ); - const lastAttemptDate = this.config.last_attempted_automatic_backup - ? new Date(this.config.last_attempted_automatic_backup) - : new Date(0); - // If no backups yet, show warning - if (!lastCompletedBackup) { + if (!lastBackup) { const description = "You have no automatic backups yet."; return html` lastCompletedDate) { @@ -151,9 +153,11 @@ class HaBackupOverviewBackups extends LitElement { `; } + const lastBackupDate = new Date(lastBackup.date); + // If last backup - if (lastCompletedBackup.failed_agent_ids?.length) { - const description = `The last automatic backup created ${relativeTime(lastCompletedDate, this.hass.locale, now, true)} wasn't stored to all locations.`; + if (lastBackup.failed_agent_ids?.length) { + const description = `The last automatic backup created ${relativeTime(lastBackupDate, this.hass.locale, now, true)} wasn't stored in all locations.`; const lastUploadedBackup = this._lastUploadedBackup(this.backups); const secondaryDescription = lastUploadedBackup @@ -179,12 +183,12 @@ class HaBackupOverviewBackups extends LitElement { `; } - const description = `Last successful backup ${relativeTime(new Date(lastCompletedBackup.date), this.hass.locale, now, true)} and stored in ${lastCompletedBackup.agent_ids?.length} locations.`; + const description = `Last successful backup ${relativeTime(lastBackupDate, this.hass.locale, now, true)} and stored in ${lastBackup.agent_ids?.length} locations.`; 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), - new Date(lastCompletedBackup.date) + lastBackupDate ); const isOverdue = From df6fe45b7d509ddb0bd819a0bbf61815126059a9 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 6 Jan 2025 16:11:50 +0100 Subject: [PATCH 3/4] Fix loading --- .../overview/ha-backup-overview-summary.ts | 11 ++++++---- src/panels/config/backup/ha-config-backup.ts | 20 ++++++++++--------- 2 files changed, 18 insertions(+), 13 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 c4040d3422e5..a47c1a700043 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 @@ -126,13 +126,17 @@ class HaBackupOverviewBackups extends LitElement { ? new Date(this.config.last_attempted_automatic_backup) : new Date(0); - const lastCompletedDate = this.config.last_attempted_automatic_backup - ? new Date(this.config.last_attempted_automatic_backup) + const lastCompletedDate = this.config.last_completed_automatic_backup + ? new Date(this.config.last_completed_automatic_backup) : new Date(0); // If last attempt is after last completed backup, show error if (lastAttemptDate > lastCompletedDate) { const description = `The last automatic backup triggered ${relativeTime(lastAttemptDate, this.hass.locale, now, true)} wasn't successful.`; + const lastUploadedBackup = this._lastUploadedBackup(this.backups); + const secondaryDescription = lastUploadedBackup + ? `Last successful backup ${relativeTime(new Date(lastUploadedBackup.date), this.hass.locale, now, true)} and stored in ${lastUploadedBackup.agent_ids?.length} locations.` + : nextBackupDescription; return html` - ${nextBackupDescription} + ${secondaryDescription} @@ -158,7 +162,6 @@ class HaBackupOverviewBackups extends LitElement { // If last backup if (lastBackup.failed_agent_ids?.length) { const description = `The last automatic backup created ${relativeTime(lastBackupDate, this.hass.locale, now, true)} wasn't stored in all locations.`; - const lastUploadedBackup = this._lastUploadedBackup(this.backups); const secondaryDescription = lastUploadedBackup ? `Last successful backup ${relativeTime(new Date(lastUploadedBackup.date), this.hass.locale, now, true)} and stored in ${lastUploadedBackup.agent_ids?.length} locations.` diff --git a/src/panels/config/backup/ha-config-backup.ts b/src/panels/config/backup/ha-config-backup.ts index 2737fd99caff..899c2e448da6 100644 --- a/src/panels/config/backup/ha-config-backup.ts +++ b/src/panels/config/backup/ha-config-backup.ts @@ -47,13 +47,7 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); - this._fetching = true; - Promise.all([this._fetchBackupInfo(), this._fetchBackupConfig()]).finally( - () => { - this._fetching = false; - } - ); - + this._fetchAll(); this.addEventListener("ha-refresh-backup-info", () => { this._fetchBackupInfo(); }); @@ -62,6 +56,15 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { }); } + private _fetchAll() { + this._fetching = true; + Promise.all([this._fetchBackupInfo(), this._fetchBackupConfig()]).finally( + () => { + this._fetching = false; + } + ); + } + public connectedCallback() { super.connectedCallback(); if (this.hasUpdated) { @@ -135,8 +138,7 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) { event.manager_state === "idle" && event.manager_state !== curState ) { - this._fetchBackupConfig(); - this._fetchBackupInfo(); + this._fetchAll(); } if ("state" in event) { if (event.state === "failed") { From 5b393fefd64452d1d80edf6a9b24cd1a95c94f2d Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Mon, 6 Jan 2025 17:18:22 +0100 Subject: [PATCH 4/4] Check attempt and completion date first --- .../overview/ha-backup-overview-summary.ts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 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 a47c1a700043..42ed30d5f521 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 @@ -100,28 +100,6 @@ class HaBackupOverviewBackups extends LitElement { this.config.schedule.state ); - // If no backups yet, show warning - if (!lastBackup) { - const description = "You have no automatic backups yet."; - return html` - - - - - ${description} - - - - ${nextBackupDescription} - - - - `; - } - const lastAttemptDate = this.config.last_attempted_automatic_backup ? new Date(this.config.last_attempted_automatic_backup) : new Date(0); @@ -157,6 +135,28 @@ class HaBackupOverviewBackups extends LitElement { `; } + // If no backups yet, show warning + if (!lastBackup) { + const description = "You have no automatic backups yet."; + return html` + + + + + ${description} + + + + ${nextBackupDescription} + + + + `; + } + const lastBackupDate = new Date(lastBackup.date); // If last backup