Improve onboarding backup restore (#23340)

* Improve onboarding backup restore

* Fix onboarding backup restore

* Fix restoring value in onboarding-restore-backup
This commit is contained in:
Wendelin 2024-12-20 12:13:16 +01:00 committed by GitHub
parent f1f53b9f24
commit 37aa2bd869
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 265 additions and 207 deletions

View File

@ -1,7 +1,7 @@
import { mdiFolderUpload } from "@mdi/js"; import { mdiFolderUpload } from "@mdi/js";
import type { TemplateResult } from "lit"; import type { TemplateResult } from "lit";
import { html, LitElement } from "lit"; import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../src/common/dom/fire_event"; import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/components/ha-circular-progress"; import "../../../src/components/ha-circular-progress";
import "../../../src/components/ha-file-upload"; import "../../../src/components/ha-file-upload";
@ -10,10 +10,12 @@ import { uploadBackup } from "../../../src/data/hassio/backup";
import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../../src/types"; import type { HomeAssistant } from "../../../src/types";
import type { LocalizeFunc } from "../../../src/common/translations/localize";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
"backup-uploaded": { backup: HassioBackup }; "backup-uploaded": { backup: HassioBackup };
"backup-cleared": void;
} }
} }
@ -21,6 +23,8 @@ declare global {
export class HassioUploadBackup extends LitElement { export class HassioUploadBackup extends LitElement {
public hass?: HomeAssistant; public hass?: HomeAssistant;
@property({ attribute: false }) public localize?: LocalizeFunc;
@state() public value: string | null = null; @state() public value: string | null = null;
@state() private _uploading = false; @state() private _uploading = false;
@ -32,13 +36,26 @@ export class HassioUploadBackup extends LitElement {
.uploading=${this._uploading} .uploading=${this._uploading}
.icon=${mdiFolderUpload} .icon=${mdiFolderUpload}
accept="application/x-tar" accept="application/x-tar"
label="Upload backup" .label=${this.localize?.(
supports="Supports .TAR files" "ui.panel.page-onboarding.restore.upload_backup"
) || "Upload backup"}
.supports=${this.localize?.(
"ui.panel.page-onboarding.restore.upload_supports"
) || "Supports .TAR files"}
.secondary=${this.localize?.(
"ui.panel.page-onboarding.restore.upload_drop"
) || "Or drop your file here"}
@file-picked=${this._uploadFile} @file-picked=${this._uploadFile}
@files-cleared=${this._clear}
></ha-file-upload> ></ha-file-upload>
`; `;
} }
private _clear() {
this.value = null;
fireEvent(this, "backup-cleared");
}
private async _uploadFile(ev) { private async _uploadFile(ev) {
const file = ev.detail.files[0]; const file = ev.detail.files[0];

View File

@ -65,7 +65,7 @@ const _computeAddons = (addons): AddonCheckboxItem[] =>
@customElement("supervisor-backup-content") @customElement("supervisor-backup-content")
export class SupervisorBackupContent extends LitElement { export class SupervisorBackupContent extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public localize?: LocalizeFunc; @property({ attribute: false }) public localize?: LocalizeFunc;
@ -186,12 +186,13 @@ export class SupervisorBackupContent extends LitElement {
.iconPath=${mdiHomeAssistant} .iconPath=${mdiHomeAssistant}
.version=${this.backup .version=${this.backup
? this.backup.homeassistant ? this.backup.homeassistant
: this.hass.config.version} : this.hass?.config.version}
> >
</supervisor-formfield-label>`} </supervisor-formfield-label>`}
> >
<ha-checkbox <ha-checkbox
.checked=${this.homeAssistant} .checked=${this.onboarding || this.homeAssistant}
.disabled=${this.onboarding}
@change=${this._toggleHomeAssistant} @change=${this._toggleHomeAssistant}
> >
</ha-checkbox> </ha-checkbox>
@ -334,7 +335,7 @@ export class SupervisorBackupContent extends LitElement {
| HassioFullBackupCreateParams { | HassioFullBackupCreateParams {
const data: any = {}; const data: any = {};
if (!this.backup) { if (!this.backup && this.hass) {
data.name = data.name =
this.backupName || this.backupName ||
formatDate(new Date(), this.hass.locale, this.hass.config); formatDate(new Date(), this.hass.locale, this.hass.config);
@ -364,7 +365,9 @@ export class SupervisorBackupContent extends LitElement {
if (folders?.length) { if (folders?.length) {
data.folders = folders; data.folders = folders;
} }
data.homeassistant = this.homeAssistant;
// onboarding needs at least homeassistant to restore
data.homeassistant = this.onboarding || this.homeAssistant;
return data; return data;
} }
@ -386,6 +389,7 @@ export class SupervisorBackupContent extends LitElement {
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder} .iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
.imageUrl=${section === "addons" && .imageUrl=${section === "addons" &&
!this.onboarding && !this.onboarding &&
this.hass &&
atLeastVersion(this.hass.config.version, 0, 105) && atLeastVersion(this.hass.config.version, 0, 105) &&
addons?.get(item.slug)?.icon addons?.get(item.slug)?.icon
? `/api/hassio/addons/${item.slug}/icon` ? `/api/hassio/addons/${item.slug}/icon`

View File

@ -8,9 +8,11 @@ import { atLeastVersion } from "../../../../src/common/config/version";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import { stopPropagation } from "../../../../src/common/dom/stop_propagation"; import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
import { slugify } from "../../../../src/common/string/slugify"; import { slugify } from "../../../../src/common/string/slugify";
import "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-md-dialog";
import "../../../../src/components/ha-dialog-header";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert"; import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-button-menu"; import "../../../../src/components/ha-button-menu";
import "../../../../src/components/ha-header-bar"; import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-icon-button"; import "../../../../src/components/ha-icon-button";
@ -19,6 +21,7 @@ import type { HassioBackupDetail } from "../../../../src/data/hassio/backup";
import { import {
fetchHassioBackupInfo, fetchHassioBackupInfo,
removeBackup, removeBackup,
restoreBackup,
} from "../../../../src/data/hassio/backup"; } from "../../../../src/data/hassio/backup";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
import { import {
@ -33,6 +36,7 @@ import "../../components/supervisor-backup-content";
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content"; import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
import type { HassioBackupDialogParams } from "./show-dialog-hassio-backup"; import type { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
import type { BackupOrRestoreKey } from "../../util/translations"; import type { BackupOrRestoreKey } from "../../util/translations";
import type { HaMdDialog } from "../../../../src/components/ha-md-dialog";
@customElement("dialog-hassio-backup") @customElement("dialog-hassio-backup")
class HassioBackupDialog class HassioBackupDialog
@ -52,13 +56,20 @@ class HassioBackupDialog
@query("supervisor-backup-content") @query("supervisor-backup-content")
private _backupContent!: SupervisorBackupContent; private _backupContent!: SupervisorBackupContent;
@query("ha-md-dialog") private _dialog?: HaMdDialog;
public async showDialog(dialogParams: HassioBackupDialogParams) { public async showDialog(dialogParams: HassioBackupDialogParams) {
this._backup = await fetchHassioBackupInfo(this.hass, dialogParams.slug);
this._dialogParams = dialogParams; this._dialogParams = dialogParams;
this._backup = await fetchHassioBackupInfo(this.hass, dialogParams.slug);
if (!this._backup) {
this._error = this._localize("no_backup_found");
} else if (this._dialogParams.onboarding && !this._backup.homeassistant) {
this._error = this._localize("restore_no_home_assistant");
}
this._restoringBackup = false; this._restoringBackup = false;
} }
public closeDialog() { private _dialogClosed(): void {
this._backup = undefined; this._backup = undefined;
this._dialogParams = undefined; this._dialogParams = undefined;
this._restoringBackup = false; this._restoringBackup = false;
@ -66,6 +77,10 @@ class HassioBackupDialog
fireEvent(this, "dialog-closed", { dialog: this.localName }); fireEvent(this, "dialog-closed", { dialog: this.localName });
} }
public closeDialog(): void {
this._dialog?.close();
}
private _localize(key: BackupOrRestoreKey) { private _localize(key: BackupOrRestoreKey) {
return ( return (
this._dialogParams!.supervisor?.localize(`backup.${key}`) || this._dialogParams!.supervisor?.localize(`backup.${key}`) ||
@ -78,100 +93,80 @@ class HassioBackupDialog
return nothing; return nothing;
} }
return html` return html`
<ha-dialog <ha-md-dialog
open open
scrimClickAction .disableCancelAction=${!this._error}
@closed=${this.closeDialog} @closed=${this._dialogClosed}
.heading=${this._backup.name}
> >
<div slot="heading"> <ha-dialog-header slot="headline">
<ha-header-bar> <ha-icon-button
<span slot="title">${this._backup.name}</span> slot="navigationIcon"
<ha-icon-button .label=${this._localize("close")}
.label=${this._localize("close")} .path=${mdiClose}
.path=${mdiClose} @click=${this.closeDialog}
slot="actionItems" .disabled=${this._restoringBackup}
dialogAction="cancel" ></ha-icon-button>
></ha-icon-button> <span slot="title" .title=${this._backup.name}
</ha-header-bar> >${this._backup.name}</span
>
${!this._dialogParams.onboarding && this._dialogParams.supervisor
? html`<ha-button-menu
slot="actionItems"
fixed
@action=${this._handleMenuAction}
@closed=${stopPropagation}
>
<ha-icon-button
.label=${this._dialogParams.supervisor.localize(
"backup.more_actions"
)}
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<mwc-list-item
>${this._dialogParams.supervisor.localize(
"backup.download_backup"
)}</mwc-list-item
>
<mwc-list-item class="error"
>${this._dialogParams.supervisor.localize(
"backup.delete_backup_title"
)}</mwc-list-item
>
</ha-button-menu>`
: nothing}
</ha-dialog-header>
<div slot="content">
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: this._restoringBackup
? html`<div class="loading">
<ha-circular-progress indeterminate></ha-circular-progress>
</div>`
: html`
<supervisor-backup-content
.hass=${this.hass}
.supervisor=${this._dialogParams.supervisor}
.backup=${this._backup}
.onboarding=${this._dialogParams.onboarding || false}
.localize=${this._dialogParams.localize}
dialogInitialFocus
>
</supervisor-backup-content>
`}
</div> </div>
${this._restoringBackup <div slot="actions">
? html`<ha-circular-progress indeterminate></ha-circular-progress>` <ha-button
: html` .disabled=${this._restoringBackup || !!this._error}
<supervisor-backup-content @click=${this._restoreClicked}
.hass=${this.hass} >
.supervisor=${this._dialogParams.supervisor} ${this._localize("restore")}
.backup=${this._backup} </ha-button>
.onboarding=${this._dialogParams.onboarding || false} </div>
.localize=${this._dialogParams.localize} </ha-md-dialog>
dialogInitialFocus
>
</supervisor-backup-content>
`}
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: nothing}
<mwc-button
.disabled=${this._restoringBackup}
slot="secondaryAction"
@click=${this._restoreClicked}
>
${this._localize("restore")}
</mwc-button>
${!this._dialogParams.onboarding && this._dialogParams.supervisor
? html`<ha-button-menu
fixed
slot="primaryAction"
@action=${this._handleMenuAction}
@closed=${stopPropagation}
>
<ha-icon-button
.label=${this._dialogParams.supervisor.localize(
"backup.more_actions"
)}
.path=${mdiDotsVertical}
slot="trigger"
></ha-icon-button>
<mwc-list-item
>${this._dialogParams.supervisor.localize(
"backup.download_backup"
)}</mwc-list-item
>
<mwc-list-item class="error"
>${this._dialogParams.supervisor.localize(
"backup.delete_backup_title"
)}</mwc-list-item
>
</ha-button-menu>`
: nothing}
</ha-dialog>
`; `;
} }
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-circular-progress {
display: block;
text-align: center;
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
display: block;
}
ha-icon-button {
color: var(--secondary-text-color);
}
`,
];
}
private _handleMenuAction(ev: CustomEvent<ActionDetail>) { private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) { switch (ev.detail.index) {
case 0: case 0:
@ -184,18 +179,9 @@ class HassioBackupDialog
} }
private async _restoreClicked() { private async _restoreClicked() {
const backupDetails = this._backupContent.backupDetails();
this._restoringBackup = true; this._restoringBackup = true;
this._dialogParams?.onRestoring?.(); const backupDetails = this._backupContent.backupDetails();
if (this._backupContent.backupType === "full") {
await this._fullRestoreClicked(backupDetails);
} else {
await this._partialRestoreClicked(backupDetails);
}
this._restoringBackup = false;
}
private async _partialRestoreClicked(backupDetails) {
const supervisor = this._dialogParams?.supervisor; const supervisor = this._dialogParams?.supervisor;
if (supervisor !== undefined && supervisor.info.state !== "running") { if (supervisor !== undefined && supervisor.info.state !== "running") {
await showAlertDialog(this, { await showAlertDialog(this, {
@ -204,91 +190,45 @@ class HassioBackupDialog
state: supervisor.info.state, state: supervisor.info.state,
}), }),
}); });
this._restoringBackup = false;
return; return;
} }
if ( if (
!(await showConfirmationDialog(this, { !(await showConfirmationDialog(this, {
title: this._localize("confirm_restore_partial_backup_title"), title: this._localize(
text: this._localize("confirm_restore_partial_backup_text"), this._backupContent.backupType === "full"
? "confirm_restore_full_backup_title"
: "confirm_restore_partial_backup_title"
),
text: this._localize(
this._backupContent.backupType === "full"
? "confirm_restore_full_backup_text"
: "confirm_restore_partial_backup_text"
),
confirmText: this._localize("restore"), confirmText: this._localize("restore"),
dismissText: this._localize("cancel"), dismissText: this._localize("cancel"),
})) }))
) { ) {
this._restoringBackup = false;
return; return;
} }
if (!this._dialogParams?.onboarding) { try {
try { await restoreBackup(
await this.hass!.callApi( this.hass,
"POST", this._backupContent.backupType,
this._backup!.slug,
`hassio/${ backupDetails,
atLeastVersion(this.hass!.config.version, 2021, 9) !!this.hass && atLeastVersion(this.hass.config.version, 2021, 9)
? "backups"
: "snapshots"
}/${this._backup!.slug}/restore/partial`,
backupDetails
);
this.closeDialog();
} catch (error: any) {
this._error = error.body.message;
}
} else {
this._dialogParams?.onRestoring?.();
await fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
method: "POST",
body: JSON.stringify(backupDetails),
});
this.closeDialog();
}
}
private async _fullRestoreClicked(backupDetails) {
const supervisor = this._dialogParams?.supervisor;
if (supervisor !== undefined && supervisor.info.state !== "running") {
await showAlertDialog(this, {
title: supervisor.localize("backup.could_not_restore"),
text: supervisor.localize("backup.restore_blocked_not_running", {
state: supervisor.info.state,
}),
});
return;
}
if (
!(await showConfirmationDialog(this, {
title: this._localize("confirm_restore_full_backup_title"),
text: this._localize("confirm_restore_full_backup_text"),
confirmText: this._localize("restore"),
dismissText: this._localize("cancel"),
}))
) {
return;
}
if (!this._dialogParams?.onboarding) {
this.hass!.callApi(
"POST",
`hassio/${
atLeastVersion(this.hass!.config.version, 2021, 9)
? "backups"
: "snapshots"
}/${this._backup!.slug}/restore/full`,
backupDetails
).then(
() => {
this.closeDialog();
},
(error) => {
this._error = error.body.message;
}
); );
} else {
this._dialogParams?.onRestoring?.(); this._dialogParams?.onRestoring?.();
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
method: "POST",
body: JSON.stringify(backupDetails),
});
this.closeDialog(); this.closeDialog();
} catch (error: any) {
this._error =
error?.body?.message || this._localize("restore_start_failed");
} finally {
this._restoringBackup = false;
} }
} }
@ -361,7 +301,36 @@ class HassioBackupDialog
private get _computeName() { private get _computeName() {
return this._backup return this._backup
? this._backup.name || this._backup.slug ? this._backup.name || this._backup.slug
: "Unnamed backup"; : this._localize("unnamed_backup");
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-circular-progress {
display: block;
text-align: center;
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
display: block;
}
ha-icon-button {
color: var(--secondary-text-color);
}
.loading {
width: 100%;
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
`,
];
} }
} }

View File

@ -15,6 +15,7 @@ import { bytesToString } from "../util/bytes-to-string";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
"file-picked": { files: File[] }; "file-picked": { files: File[] };
"files-cleared": void;
} }
} }
@ -216,6 +217,7 @@ export class HaFileUpload extends LitElement {
this._input!.value = ""; this._input!.value = "";
this.value = undefined; this.value = undefined;
fireEvent(this, "change"); fireEvent(this, "change");
fireEvent(this, "files-cleared");
} }
static get styles() { static get styles() {

View File

@ -1,5 +1,6 @@
import { atLeastVersion } from "../../common/config/version"; import { atLeastVersion } from "../../common/config/version";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { handleFetchPromise } from "../../util/hass-call-api";
import type { HassioResponse } from "./common"; import type { HassioResponse } from "./common";
import { hassioApiResultExtractor } from "./common"; import { hassioApiResultExtractor } from "./common";
@ -105,11 +106,13 @@ export const fetchHassioBackupInfo = async (
); );
} }
// When called from onboarding we don't have hass // When called from onboarding we don't have hass
const resp = await fetch(`/api/hassio/backups/${backup}/info`, { return hassioApiResultExtractor(
method: "GET", await handleFetchPromise(
}); fetch(`/api/hassio/backups/${backup}/info`, {
const data = (await resp.json()).data; method: "GET",
return data; })
)
);
}; };
export const reloadHassioBackups = async (hass: HomeAssistant) => { export const reloadHassioBackups = async (hass: HomeAssistant) => {
@ -236,3 +239,26 @@ export const uploadBackup = async (
} }
return resp.json(); return resp.json();
}; };
export const restoreBackup = async (
hass: HomeAssistant | undefined,
type: HassioBackupDetail["type"],
backupSlug: string,
backupDetails: HassioPartialBackupCreateParams | HassioFullBackupCreateParams,
useSnapshotUrl: boolean
): Promise<void> => {
if (hass) {
await hass.callApi<HassioResponse<{ job_id: string }>>(
"POST",
`hassio/${useSnapshotUrl ? "snapshots" : "backups"}/${backupSlug}/restore/${type}`,
backupDetails
);
} else {
await handleFetchPromise(
fetch(`/api/hassio/backups/${backupSlug}/restore/${type}`, {
method: "POST",
body: JSON.stringify(backupDetails),
})
);
}
};

View File

@ -1,12 +1,13 @@
import "@material/mwc-button/mwc-button";
import type { CSSResultGroup, TemplateResult } from "lit"; import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { showHassioBackupDialog } from "../../hassio/src/dialogs/backup/show-dialog-hassio-backup"; import { showHassioBackupDialog } from "../../hassio/src/dialogs/backup/show-dialog-hassio-backup";
import "../../hassio/src/components/hassio-upload-backup"; import "../../hassio/src/components/hassio-upload-backup";
import type { LocalizeFunc } from "../common/translations/localize"; import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-ansi-to-html"; import "../components/ha-ansi-to-html";
import "../components/ha-card"; import "../components/ha-card";
import "../components/ha-alert";
import "../components/ha-button";
import { fetchInstallationType } from "../data/onboarding"; import { fetchInstallationType } from "../data/onboarding";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import "./onboarding-loading"; import "./onboarding-loading";
@ -16,32 +17,49 @@ import { navigate } from "../common/navigate";
@customElement("onboarding-restore-backup") @customElement("onboarding-restore-backup")
class OnboardingRestoreBackup extends LitElement { class OnboardingRestoreBackup extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public localize!: LocalizeFunc; @property({ attribute: false }) public localize!: LocalizeFunc;
@property() public language!: string; @property() public language!: string;
@state() public _restoring = false; @state() private _restoring = false;
@state() private _backupSlug?: string;
protected render(): TemplateResult { protected render(): TemplateResult {
return html`${this._restoring return html`
${this._restoring
? html`<h1> ? html`<h1>
${this.localize("ui.panel.page-onboarding.restore.in_progress")} ${this.localize("ui.panel.page-onboarding.restore.in_progress")}
</h1> </h1>
<ha-alert alert-type="info">
${this.localize("ui.panel.page-onboarding.restore.in_progress")}
</ha-alert>
<onboarding-loading></onboarding-loading>` <onboarding-loading></onboarding-loading>`
: html` <h1> : html` <h1>
${this.localize("ui.panel.page-onboarding.restore.header")} ${this.localize("ui.panel.page-onboarding.restore.header")}
</h1> </h1>
<hassio-upload-backup <hassio-upload-backup
@backup-uploaded=${this._backupUploaded} @backup-uploaded=${this._backupUploaded}
@backup-cleared=${this._backupCleared}
.hass=${this.hass} .hass=${this.hass}
.localize=${this.localize}
></hassio-upload-backup>`} ></hassio-upload-backup>`}
<div class="footer"> <div class="footer">
<mwc-button @click=${this._back} .disabled=${this._restoring}> <ha-button @click=${this._back} .disabled=${this._restoring}>
${this.localize("ui.panel.page-onboarding.back")} ${this.localize("ui.panel.page-onboarding.back")}
</mwc-button> </ha-button>
</div> `; ${this._backupSlug
? html`<ha-button
@click=${this._showBackupDialog}
.disabled=${this._restoring}
>
${this.localize("ui.panel.page-onboarding.restore.restore")}
</ha-button>`
: nothing}
</div>
`;
} }
private _back(): void { private _back(): void {
@ -50,12 +68,16 @@ class OnboardingRestoreBackup extends LitElement {
private _backupUploaded(ev) { private _backupUploaded(ev) {
const backup = ev.detail.backup; const backup = ev.detail.backup;
this._showBackupDialog(backup.slug); this._backupSlug = backup.slug;
this._showBackupDialog();
}
private _backupCleared() {
this._backupSlug = undefined;
} }
protected firstUpdated(changedProps) { protected firstUpdated(changedProps) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
setInterval(() => this._checkRestoreStatus(), 1000);
} }
private async _checkRestoreStatus(): Promise<void> { private async _checkRestoreStatus(): Promise<void> {
@ -73,13 +95,18 @@ class OnboardingRestoreBackup extends LitElement {
} }
} }
private _showBackupDialog(slug: string): void { private _scheduleCheckRestoreStatus(): void {
setTimeout(() => this._checkRestoreStatus(), 1000);
}
private _showBackupDialog(): void {
showHassioBackupDialog(this, { showHassioBackupDialog(this, {
slug, slug: this._backupSlug!,
onboarding: true, onboarding: true,
localize: this.localize, localize: this.localize,
onRestoring: () => { onRestoring: () => {
this._restoring = true; this._restoring = true;
this._scheduleCheckRestoreStatus();
}, },
}); });
} }
@ -97,8 +124,9 @@ class OnboardingRestoreBackup extends LitElement {
width: 100%; width: 100%;
} }
.footer { .footer {
display: flex;
justify-content: space-between;
width: 100%; width: 100%;
text-align: left;
} }
`, `,
]; ];

View File

@ -7587,7 +7587,10 @@
"restore": { "restore": {
"header": "Restore a backup", "header": "Restore a backup",
"in_progress": "Restore in progress", "in_progress": "Restore in progress",
"failed": "Restore failed",
"upload_backup": "[%key:supervisor::backup::upload_backup%]", "upload_backup": "[%key:supervisor::backup::upload_backup%]",
"upload_supports": "Supports .TAR files",
"upload_drop": "[%key:ui::components::file-upload::secondary%]",
"show_log": "Show full log", "show_log": "Show full log",
"hide_log": "Hide full log", "hide_log": "Hide full log",
"full_backup": "[%key:supervisor::backup::full_backup%]", "full_backup": "[%key:supervisor::backup::full_backup%]",
@ -7601,12 +7604,17 @@
"password": "[%key:supervisor::backup::password%]", "password": "[%key:supervisor::backup::password%]",
"confirm_password": "[%key:supervisor::backup::confirm_password%]", "confirm_password": "[%key:supervisor::backup::confirm_password%]",
"confirm_restore_partial_backup_title": "[%key:supervisor::backup::confirm_restore_partial_backup_title%]", "confirm_restore_partial_backup_title": "[%key:supervisor::backup::confirm_restore_partial_backup_title%]",
"confirm_restore_partial_backup_text": "[%key:supervisor::backup::confirm_restore_partial_backup_text%]", "confirm_restore_partial_backup_text": "The backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again and you see the login screen. If it fails it will bring you back to the onboarding.",
"confirm_restore_full_backup_title": "[%key:supervisor::backup::confirm_restore_full_backup_title%]", "confirm_restore_full_backup_title": "[%key:supervisor::backup::confirm_restore_full_backup_title%]",
"confirm_restore_full_backup_text": "[%key:supervisor::backup::confirm_restore_full_backup_text%]", "confirm_restore_full_backup_text": "Your entire system will be wiped and the backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again and you see the login screen. If it fails it will bring you back to the onboarding.",
"restore": "[%key:supervisor::backup::restore%]", "restore": "[%key:supervisor::backup::restore%]",
"close": "[%key:ui::common::close%]", "close": "[%key:ui::common::close%]",
"cancel": "[%key:ui::common::cancel%]" "cancel": "[%key:ui::common::cancel%]",
"retry": "Retry",
"restore_start_failed": "[%key:supervisor::backup::restore_start_failed%]",
"no_backup_found": "[%key:supervisor::backup::no_backup_found%]",
"restore_no_home_assistant": "[%key:supervisor::backup::restore_no_home_assistant%]",
"unnamed_backup": "[%key:supervisor::backup::unnamed_backup%]"
} }
}, },
"custom": { "custom": {
@ -8092,16 +8100,16 @@
"addons": "Add-ons", "addons": "Add-ons",
"folders": "Folders", "folders": "Folders",
"size": "Size", "size": "Size",
"password": "Backup password", "password": "Backup encryption key",
"confirm_password": "Confirm backup password", "confirm_password": "Confirm encryption key",
"password_protection": "Password protection", "password_protection": "Password protection",
"enter_password": "Please enter a password.", "enter_password": "Please enter a password.",
"passwords_not_matching": "The passwords does not match", "passwords_not_matching": "The passwords does not match",
"backup_already_running": "A backup or restore is already running. Creating a new backup is currently not possible, try again later.", "backup_already_running": "A backup or restore is already running. Creating a new backup is currently not possible, try again later.",
"confirm_restore_partial_backup_title": "Restore partial backup", "confirm_restore_partial_backup_title": "Restore partial backup",
"confirm_restore_partial_backup_text": "The backup will be restored. Depending on the size of the backup, this can take up to 45 min.", "confirm_restore_partial_backup_text": "The backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again.",
"confirm_restore_full_backup_title": "Restore full backup", "confirm_restore_full_backup_title": "Restore full backup",
"confirm_restore_full_backup_text": "Your entire system will be wiped and the backup will be restored. Depending on the size of the backup, this can take up to 45 min.", "confirm_restore_full_backup_text": "Your entire system will be wiped and the backup will be restored. Depending on the size of the backup, this can take up to 45 min. Home Assistant needs to shutdown and the restore progress is running in the background. If it succeeds, Home Assistant will automatically start again.",
"confirm_delete_title": "Delete backup", "confirm_delete_title": "Delete backup",
"confirm_delete_text": "This backup will be permanently deleted and cannot be restored later.", "confirm_delete_text": "This backup will be permanently deleted and cannot be restored later.",
"restore": "Restore", "restore": "Restore",
@ -8111,7 +8119,11 @@
"download": "Download", "download": "Download",
"more_actions": "More actions", "more_actions": "More actions",
"remote_download_title": "Potentially slow download", "remote_download_title": "Potentially slow download",
"remote_download_text": "You are accessing Home Assistant via remote access. Downloading backups over the Nabu Casa URL will take some time. If you are at home, cancel this dialog and enter your local URL, such as 'http://homeassistant.local:8123'" "remote_download_text": "You are accessing Home Assistant via remote access. Downloading backups over the Nabu Casa URL will take some time. If you are at home, cancel this dialog and enter your local URL, such as 'http://homeassistant.local:8123'",
"restore_start_failed": "Failed to start restore. Unknown error.",
"no_backup_found": "No backup found.",
"restore_no_home_assistant": "Backup does not contain Home Assistant data. To restore Home Assistant you need a backup of Home Assistant core.",
"unnamed_backup": "Unnamed backup"
}, },
"dialog": { "dialog": {
"network": { "network": {