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..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 @@ -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 _lastBackup = 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,28 @@ class HaBackupOverviewBackups extends LitElement { `; } - const lastSuccessfulBackup = this._lastSuccessfulBackup(this.backups); + const lastBackup = this._lastBackup(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 + const lastCompletedDate = this.config.last_completed_automatic_backup ? new Date(this.config.last_completed_automatic_backup) - : undefined; - - const now = new Date(); + : new Date(0); - 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.`; + 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 (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} + ${secondaryDescription} `; } - const nextBackupDescription = this._nextBackupDescription( - this.config.schedule.state - ); - - if (!lastSuccessfulBackup) { + // If no backups yet, show warning + if (!lastBackup) { + const description = "You have no automatic backups yet."; return html` + + + ${description} + ${nextBackupDescription} @@ -140,10 +157,41 @@ class HaBackupOverviewBackups extends LitElement { `; } + const lastBackupDate = new Date(lastBackup.date); + + // 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.` + : nextBackupDescription; + + return html` + + + + + ${description} + + + + ${secondaryDescription} + + + + `; + } + + 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(lastSuccessfulBackup.date) + lastBackupDate ); const isOverdue = @@ -160,7 +208,7 @@ class HaBackupOverviewBackups extends LitElement { - ${lastBackupDescription} + ${description} @@ -170,12 +218,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..899c2e448da6 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 { @@ -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) { @@ -128,11 +131,16 @@ 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._fetchAll(); + } if ("state" in event) { - if (event.state === "completed" || event.state === "failed") { - this._fetchBackupInfo(); - } if (event.state === "failed") { let message = ""; switch (this._manager.manager_state) {