Compare commits

...

1 Commits

Author SHA1 Message Date
Petar Petrov 128c5bddeb Show progress on the Zigbee backup button while the backup is created 2026-07-31 13:25:14 +03:00
4 changed files with 49 additions and 21 deletions
@@ -23,7 +23,7 @@ export class DemoHaProgressButton extends LitElement {
<ha-progress-button @click=${this._clickedFail}>
Fail
</ha-progress-button>
<ha-progress-button size="small" @click=${this._clickedSuccess}>
<ha-progress-button size="s" @click=${this._clickedSuccess}>
small
</ha-progress-button>
<ha-progress-button
@@ -18,6 +18,8 @@ export class HaProgressButton extends LitElement {
@property() appearance: Appearance = "accent";
@property() size: "xs" | "s" | "m" | "l" | "xl" = "m";
@property({ attribute: false }) public iconPath?: string;
@property() variant: "brand" | "danger" | "neutral" | "warning" | "success" =
@@ -32,6 +34,7 @@ export class HaProgressButton extends LitElement {
return html`
<ha-button
.appearance=${appearance}
.size=${this.size}
.disabled=${this.disabled}
.loading=${this.progress}
.variant=${
@@ -118,6 +121,12 @@ export class HaProgressButton extends LitElement {
width: 100%;
}
/* The icon lives in this shadow root, so callers cannot size it themselves. */
ha-button[size="xs"] ha-svg-icon[slot="start"],
ha-button[size="s"] ha-svg-icon[slot="start"] {
--mdc-icon-size: 16px;
}
ha-button.result::part(start),
ha-button.result::part(end),
ha-button.result::part(label),
@@ -19,6 +19,8 @@ import { animationStyles } from "../../../../../resources/theme/animations.globa
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
@@ -67,6 +69,8 @@ class ZHAConfigDashboard extends LitElement {
@state() private _error?: string;
@state() private _generatingBackup = false;
protected firstUpdated(changedProperties: PropertyValues<this>) {
super.firstUpdated(changedProperties);
if (!this.hass) {
@@ -355,17 +359,18 @@ class ZHAConfigDashboard extends LitElement {
"ui.panel.config.zha.configuration_page.download_backup_description"
)}
</span>
<ha-button
<ha-progress-button
appearance="plain"
slot="end"
size="s"
.iconPath=${mdiDownload}
.progress=${this._generatingBackup}
@click=${this._createAndDownloadBackup}
>
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.zha.configuration_page.download_backup_action"
)}
</ha-button>
</ha-progress-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
@@ -408,30 +413,29 @@ class ZHAConfigDashboard extends LitElement {
this._configuration = await fetchZHAConfiguration(this.hass!);
}
private async _createAndDownloadBackup(): Promise<void> {
private async _createAndDownloadBackup(ev: Event): Promise<void> {
const button = ev.currentTarget as HaProgressButton;
let backup_and_metadata: ZHANetworkBackupAndMetadata;
// Reading the backup from the coordinator can take 5-30 seconds.
this._generatingBackup = true;
try {
backup_and_metadata = await createZHANetworkBackup(this.hass!);
} catch (err: any) {
button.actionError();
showAlertDialog(this, {
title: "Failed to create backup",
title: this.hass.localize(
"ui.panel.config.zha.configuration_page.backup_failed"
),
text: err.message,
warning: true,
});
return;
} finally {
this._generatingBackup = false;
}
if (!backup_and_metadata.is_complete) {
await showAlertDialog(this, {
title: "Backup is incomplete",
text: "A backup has been created but it is incomplete and cannot be restored. This is a coordinator firmware limitation.",
});
}
const backupJSON: string =
"data:text/plain;charset=utf-8," +
encodeURIComponent(JSON.stringify(backup_and_metadata.backup, null, 4));
const backupTime: Date = new Date(
Date.parse(backup_and_metadata.backup.backup_time)
);
@@ -441,7 +445,23 @@ class ZHAConfigDashboard extends LitElement {
basename = `Incomplete ${basename}`;
}
fileDownload(backupJSON, `${basename}.json`);
const blob = new Blob(
[JSON.stringify(backup_and_metadata.backup, null, 4)],
{ type: "application/json" }
);
fileDownload(URL.createObjectURL(blob), `${basename}.json`);
button.actionSuccess();
if (!backup_and_metadata.is_complete) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.zha.configuration_page.backup_incomplete_title"
),
text: this.hass.localize(
"ui.panel.config.zha.configuration_page.backup_incomplete_text"
),
});
}
}
private _openOptionFlow() {
@@ -517,10 +537,6 @@ class ZHAConfigDashboard extends LitElement {
--md-item-overflow: visible;
}
ha-button[size="s"] ha-svg-icon {
--mdc-icon-size: 16px;
}
.network-status div.heading {
display: flex;
align-items: center;
+3
View File
@@ -7557,6 +7557,9 @@
"download_backup": "Download backup",
"download_backup_description": "Save your Zigbee network configuration to a file",
"download_backup_action": "Download",
"backup_failed": "Failed to create backup",
"backup_incomplete_title": "Backup is incomplete",
"backup_incomplete_text": "A backup has been created but it is incomplete and cannot be restored. This is a limitation of your adapter's firmware.",
"migrate_radio": "Migrate adapter",
"migrate_radio_description": "Move your Zigbee network to a different adapter",
"migrate_radio_action": "Migrate",