mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 01:36:49 +00:00
Setup backup overview page (#23331)
* Add overview page * Remove configure button * Reorganize files * Add backups summary * Add settings overview * Fixes * Update wording * Setup onboarding before creating a backup
This commit is contained in:
parent
3da13b823a
commit
95559cbc2a
@ -214,7 +214,7 @@ export const getPreferredAgentForDownload = (agents: string[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CORE_LOCAL_AGENT = "backup.local";
|
export const CORE_LOCAL_AGENT = "backup.local";
|
||||||
export const HASSIO_LOCAL_AGENT = "backup.hassio";
|
export const HASSIO_LOCAL_AGENT = "hassio.local";
|
||||||
export const CLOUD_AGENT = "cloud.cloud";
|
export const CLOUD_AGENT = "cloud.cloud";
|
||||||
|
|
||||||
export const isLocalAgent = (agentId: string) =>
|
export const isLocalAgent = (agentId: string) =>
|
||||||
|
@ -2,22 +2,22 @@ import { mdiDatabase } from "@mdi/js";
|
|||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../../../common/entity/compute_domain";
|
||||||
import "../../../../components/ha-md-list";
|
import "../../../../../components/ha-md-list";
|
||||||
import "../../../../components/ha-md-list-item";
|
import "../../../../../components/ha-md-list-item";
|
||||||
import "../../../../components/ha-svg-icon";
|
import "../../../../../components/ha-svg-icon";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../../components/ha-switch";
|
||||||
import {
|
import {
|
||||||
CLOUD_AGENT,
|
CLOUD_AGENT,
|
||||||
compareAgents,
|
compareAgents,
|
||||||
computeBackupAgentName,
|
computeBackupAgentName,
|
||||||
fetchBackupAgentsInfo,
|
fetchBackupAgentsInfo,
|
||||||
isLocalAgent,
|
isLocalAgent,
|
||||||
} from "../../../../data/backup";
|
} from "../../../../../data/backup";
|
||||||
import type { CloudStatus } from "../../../../data/cloud";
|
import type { CloudStatus } from "../../../../../data/cloud";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import { brandsUrl } from "../../../../util/brands-url";
|
import { brandsUrl } from "../../../../../util/brands-url";
|
||||||
|
|
||||||
const DEFAULT_AGENTS = [];
|
const DEFAULT_AGENTS = [];
|
||||||
|
|
@ -9,21 +9,21 @@ import type { PropertyValues } from "lit";
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../../components/ha-button";
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-md-list";
|
import "../../../../../components/ha-md-list";
|
||||||
import "../../../../components/ha-md-list-item";
|
import "../../../../../components/ha-md-list-item";
|
||||||
import "../../../../components/ha-md-select";
|
import "../../../../../components/ha-md-select";
|
||||||
import type { HaMdSelect } from "../../../../components/ha-md-select";
|
import type { HaMdSelect } from "../../../../../components/ha-md-select";
|
||||||
import "../../../../components/ha-md-select-option";
|
import "../../../../../components/ha-md-select-option";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../../components/ha-switch";
|
||||||
import type { HaSwitch } from "../../../../components/ha-switch";
|
import type { HaSwitch } from "../../../../../components/ha-switch";
|
||||||
import { fetchHassioAddonsInfo } from "../../../../data/hassio/addon";
|
import { fetchHassioAddonsInfo } from "../../../../../data/hassio/addon";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import "./ha-backup-addons-picker";
|
import "../ha-backup-addons-picker";
|
||||||
import type { BackupAddonItem } from "./ha-backup-addons-picker";
|
import type { BackupAddonItem } from "../ha-backup-addons-picker";
|
||||||
|
|
||||||
export type FormData = {
|
export type FormData = {
|
||||||
homeassistant: boolean;
|
homeassistant: boolean;
|
@ -1,13 +1,13 @@
|
|||||||
import { mdiDownload } from "@mdi/js";
|
import { mdiDownload } from "@mdi/js";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-md-list";
|
import "../../../../../components/ha-md-list";
|
||||||
import "../../../../components/ha-md-list-item";
|
import "../../../../../components/ha-md-list-item";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import { showChangeBackupEncryptionKeyDialog } from "../dialogs/show-dialog-change-backup-encryption-key";
|
import { showChangeBackupEncryptionKeyDialog } from "../../dialogs/show-dialog-change-backup-encryption-key";
|
||||||
import { fileDownload } from "../../../../util/file_download";
|
import { fileDownload } from "../../../../../util/file_download";
|
||||||
import { showSetBackupEncryptionKeyDialog } from "../dialogs/show-dialog-set-backup-encryption-key";
|
import { showSetBackupEncryptionKeyDialog } from "../../dialogs/show-dialog-set-backup-encryption-key";
|
||||||
|
|
||||||
@customElement("ha-backup-config-encryption-key")
|
@customElement("ha-backup-config-encryption-key")
|
||||||
class HaBackupConfigEncryptionKey extends LitElement {
|
class HaBackupConfigEncryptionKey extends LitElement {
|
@ -2,19 +2,19 @@ import type { PropertyValues } from "lit";
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
import type { HaCheckbox } from "../../../../components/ha-checkbox";
|
import type { HaCheckbox } from "../../../../../components/ha-checkbox";
|
||||||
import "../../../../components/ha-md-list";
|
import "../../../../../components/ha-md-list";
|
||||||
import "../../../../components/ha-md-list-item";
|
import "../../../../../components/ha-md-list-item";
|
||||||
import "../../../../components/ha-md-select";
|
import "../../../../../components/ha-md-select";
|
||||||
import "../../../../components/ha-md-textfield";
|
import "../../../../../components/ha-md-textfield";
|
||||||
import type { HaMdSelect } from "../../../../components/ha-md-select";
|
import type { HaMdSelect } from "../../../../../components/ha-md-select";
|
||||||
import "../../../../components/ha-md-select-option";
|
import "../../../../../components/ha-md-select-option";
|
||||||
import "../../../../components/ha-switch";
|
import "../../../../../components/ha-switch";
|
||||||
import type { BackupConfig } from "../../../../data/backup";
|
import type { BackupConfig } from "../../../../../data/backup";
|
||||||
import { BackupScheduleState } from "../../../../data/backup";
|
import { BackupScheduleState } from "../../../../../data/backup";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
import { clamp } from "../../../../common/number/clamp";
|
import { clamp } from "../../../../../common/number/clamp";
|
||||||
|
|
||||||
export type BackupConfigSchedule = Pick<BackupConfig, "schedule" | "retention">;
|
export type BackupConfigSchedule = Pick<BackupConfig, "schedule" | "retention">;
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ const MAX_VALUE = 50;
|
|||||||
enum RetentionPreset {
|
enum RetentionPreset {
|
||||||
COPIES_3 = "copies_3",
|
COPIES_3 = "copies_3",
|
||||||
DAYS_7 = "days_7",
|
DAYS_7 = "days_7",
|
||||||
FOREOVER = "forever",
|
FOREVER = "forever",
|
||||||
CUSTOM = "custom",
|
CUSTOM = "custom",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ class HaBackupConfigSchedule extends LitElement {
|
|||||||
<ha-md-select-option .value=${RetentionPreset.DAYS_7}>
|
<ha-md-select-option .value=${RetentionPreset.DAYS_7}>
|
||||||
<div slot="headline">Keep 7 days</div>
|
<div slot="headline">Keep 7 days</div>
|
||||||
</ha-md-select-option>
|
</ha-md-select-option>
|
||||||
<ha-md-select-option .value=${RetentionPreset.FOREOVER}>
|
<ha-md-select-option .value=${RetentionPreset.FOREVER}>
|
||||||
<div slot="headline">Keep forever</div>
|
<div slot="headline">Keep forever</div>
|
||||||
</ha-md-select-option>
|
</ha-md-select-option>
|
||||||
<ha-md-select-option .value=${RetentionPreset.CUSTOM}>
|
<ha-md-select-option .value=${RetentionPreset.CUSTOM}>
|
||||||
@ -270,7 +270,9 @@ class HaBackupConfigSchedule extends LitElement {
|
|||||||
const data = this._getData(this.value);
|
const data = this._getData(this.value);
|
||||||
const retention = RETENTION_PRESETS[value];
|
const retention = RETENTION_PRESETS[value];
|
||||||
// Ensure we have at least 1 in defaut value because user can't select 0
|
// Ensure we have at least 1 in defaut value because user can't select 0
|
||||||
retention.value = Math.max(retention.value, 1);
|
if (value !== RetentionPreset.FOREVER) {
|
||||||
|
retention.value = Math.max(retention.value, 1);
|
||||||
|
}
|
||||||
this._setData({
|
this._setData({
|
||||||
...data,
|
...data,
|
||||||
retention: RETENTION_PRESETS[value],
|
retention: RETENTION_PRESETS[value],
|
@ -0,0 +1,122 @@
|
|||||||
|
import type { CSSResultGroup } from "lit";
|
||||||
|
import { css, html, LitElement } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { navigate } from "../../../../../common/navigate";
|
||||||
|
import "../../../../../components/ha-button";
|
||||||
|
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 { haStyle } from "../../../../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
|
import { bytesToString } from "../../../../../util/bytes-to-string";
|
||||||
|
|
||||||
|
type BackupStats = {
|
||||||
|
count: number;
|
||||||
|
size: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const computeBackupStats = (backups: BackupContent[]): BackupStats =>
|
||||||
|
backups.reduce(
|
||||||
|
(stats, backup) => {
|
||||||
|
stats.count++;
|
||||||
|
stats.size += backup.size;
|
||||||
|
return stats;
|
||||||
|
},
|
||||||
|
{ count: 0, size: 0 }
|
||||||
|
);
|
||||||
|
|
||||||
|
@customElement("ha-backup-overview-backups")
|
||||||
|
class HaBackupOverviewBackups extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public backups: BackupContent[] = [];
|
||||||
|
|
||||||
|
private _showAll() {
|
||||||
|
navigate("/config/backup/backups");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _automaticStats = memoizeOne((backups: BackupContent[]) => {
|
||||||
|
const automaticBackups = backups.filter(
|
||||||
|
(backup) => backup.with_automatic_settings
|
||||||
|
);
|
||||||
|
return computeBackupStats(automaticBackups);
|
||||||
|
});
|
||||||
|
|
||||||
|
private _manualStats = memoizeOne((backups: BackupContent[]) => {
|
||||||
|
const manualBackups = backups.filter(
|
||||||
|
(backup) => !backup.with_automatic_settings
|
||||||
|
);
|
||||||
|
return computeBackupStats(manualBackups);
|
||||||
|
});
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const automaticStats = this._automaticStats(this.backups);
|
||||||
|
const manualStats = this._manualStats(this.backups);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card class="my-backups">
|
||||||
|
<div class="card-header">My backups</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<ha-md-list>
|
||||||
|
<ha-md-list-item type="link" href="/config/backup/backups">
|
||||||
|
<div slot="headline">
|
||||||
|
${automaticStats.count} automatic backups
|
||||||
|
</div>
|
||||||
|
<div slot="supporting-text">
|
||||||
|
${bytesToString(automaticStats.size, 1)} in total
|
||||||
|
</div>
|
||||||
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
|
</ha-md-list-item>
|
||||||
|
<ha-md-list-item type="link" href="/config/backup/backups">
|
||||||
|
<div slot="headline">${manualStats.count} manual backups</div>
|
||||||
|
<div slot="supporting-text">
|
||||||
|
${bytesToString(manualStats.size, 1)} in total
|
||||||
|
</div>
|
||||||
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
|
</ha-md-list-item>
|
||||||
|
</ha-md-list>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<ha-button href="/config/backup/backups" @click=${this._showAll}>
|
||||||
|
Show all backups
|
||||||
|
</ha-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.content {
|
||||||
|
padding: 28px 20px 0;
|
||||||
|
max-width: 690px;
|
||||||
|
margin: 0 auto;
|
||||||
|
gap: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
margin-bottom: 72px;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-backup-overview-backups": HaBackupOverviewBackups;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
import { mdiCalendar, mdiCog, mdiPuzzle, mdiUpload } from "@mdi/js";
|
||||||
|
import type { CSSResultGroup } from "lit";
|
||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { navigate } from "../../../../../common/navigate";
|
||||||
|
import "../../../../../components/ha-button";
|
||||||
|
import "../../../../../components/ha-card";
|
||||||
|
import "../../../../../components/ha-md-list";
|
||||||
|
import "../../../../../components/ha-md-list-item";
|
||||||
|
import "../../../../../components/ha-svg-icon";
|
||||||
|
import type { BackupConfig } from "../../../../../data/backup";
|
||||||
|
import { BackupScheduleState, isLocalAgent } from "../../../../../data/backup";
|
||||||
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../../../types";
|
||||||
|
|
||||||
|
@customElement("ha-backup-overview-settings")
|
||||||
|
class HaBackupBackupsSummary extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public config!: BackupConfig;
|
||||||
|
|
||||||
|
private _configure() {
|
||||||
|
navigate("/config/backup/settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _scheduleDescription(config: BackupConfig): string {
|
||||||
|
const { copies, days } = config.retention;
|
||||||
|
const { state: schedule } = config.schedule;
|
||||||
|
|
||||||
|
if (schedule === BackupScheduleState.NEVER) {
|
||||||
|
return "Automatic backups are disabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
let copiesText = "and keep all backups";
|
||||||
|
if (copies) {
|
||||||
|
copiesText = `and keep the latest ${copies} copie(s)`;
|
||||||
|
} else if (days) {
|
||||||
|
copiesText = `and keep backups for ${days} day(s)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scheduleText = "";
|
||||||
|
if (schedule === BackupScheduleState.DAILY) {
|
||||||
|
scheduleText = `Daily at 04:45`;
|
||||||
|
}
|
||||||
|
if (schedule === BackupScheduleState.MONDAY) {
|
||||||
|
scheduleText = `Weekly on Mondays at 04:45`;
|
||||||
|
}
|
||||||
|
if (schedule === BackupScheduleState.TUESDAY) {
|
||||||
|
scheduleText = `Weekly on Thuesdays at 04:45`;
|
||||||
|
}
|
||||||
|
if (schedule === BackupScheduleState.WEDNESDAY) {
|
||||||
|
scheduleText = `Weekly on Wednesdays at 04:45`;
|
||||||
|
}
|
||||||
|
if (schedule === BackupScheduleState.THURSDAY) {
|
||||||
|
scheduleText = `Weekly on Thursdays at 04:45`;
|
||||||
|
}
|
||||||
|
if (schedule === BackupScheduleState.FRIDAY) {
|
||||||
|
scheduleText = `Weekly on Fridays at 04:45`;
|
||||||
|
}
|
||||||
|
if (schedule === BackupScheduleState.SATURDAY) {
|
||||||
|
scheduleText = `Weekly on Saturdays at 04:45`;
|
||||||
|
}
|
||||||
|
if (schedule === BackupScheduleState.SUNDAY) {
|
||||||
|
scheduleText = `Weekly on Sundays at 04:45`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scheduleText + " " + copiesText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addonsDescription(config: BackupConfig): string {
|
||||||
|
if (config.create_backup.include_all_addons) {
|
||||||
|
return "All add-ons, including new";
|
||||||
|
}
|
||||||
|
if (config.create_backup.include_addons?.length) {
|
||||||
|
return `${config.create_backup.include_addons.length} add-ons`;
|
||||||
|
}
|
||||||
|
return "No add-ons";
|
||||||
|
}
|
||||||
|
|
||||||
|
private _agentsDescription(config: BackupConfig): string {
|
||||||
|
const hasLocal = config.create_backup.agent_ids.some((a) =>
|
||||||
|
isLocalAgent(a)
|
||||||
|
);
|
||||||
|
|
||||||
|
const offsiteLocations = config.create_backup.agent_ids.filter(
|
||||||
|
(a) => !isLocalAgent(a)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (offsiteLocations.length) {
|
||||||
|
return `Upload to ${offsiteLocations.length} off-site locations`;
|
||||||
|
}
|
||||||
|
if (hasLocal) {
|
||||||
|
return "Local backup only";
|
||||||
|
}
|
||||||
|
return "No location configured";
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const isHassio = this.hass.config.components.includes("hassio");
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card class="my-backups">
|
||||||
|
<div class="card-header">Automatic backups</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<ha-md-list>
|
||||||
|
<ha-md-list-item>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiCalendar}></ha-svg-icon>
|
||||||
|
<div slot="headline">
|
||||||
|
${this._scheduleDescription(this.config)}
|
||||||
|
</div>
|
||||||
|
<div slot="supporting-text">
|
||||||
|
Schedule and number of backups to keep
|
||||||
|
</div>
|
||||||
|
</ha-md-list-item>
|
||||||
|
<ha-md-list-item>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
||||||
|
<div slot="headline">
|
||||||
|
${this.config.create_backup.include_database
|
||||||
|
? "Settings and history"
|
||||||
|
: "Settings only"}
|
||||||
|
</div>
|
||||||
|
<div slot="supporting-text">
|
||||||
|
Home Assistant data that is included
|
||||||
|
</div>
|
||||||
|
</ha-md-list-item>
|
||||||
|
${isHassio
|
||||||
|
? html`
|
||||||
|
<ha-md-list-item>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiPuzzle}></ha-svg-icon>
|
||||||
|
<div slot="headline">
|
||||||
|
${this._addonsDescription(this.config)}
|
||||||
|
</div>
|
||||||
|
<div slot="supporting-text">Add-ons that are included</div>
|
||||||
|
</ha-md-list-item>
|
||||||
|
<ha-md-list-item>
|
||||||
|
<ha-svg-icon slot="start" .path=${mdiUpload}></ha-svg-icon>
|
||||||
|
<div slot="headline">
|
||||||
|
${this._agentsDescription(this.config)}
|
||||||
|
</div>
|
||||||
|
<div slot="supporting-text">
|
||||||
|
Locations where backup is uploaded to
|
||||||
|
</div>
|
||||||
|
</ha-md-list-item>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
</ha-md-list>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<ha-button @click=${this._configure}>
|
||||||
|
Configure automatic backups
|
||||||
|
</ha-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.content {
|
||||||
|
padding: 28px 20px 0;
|
||||||
|
max-width: 690px;
|
||||||
|
margin: 0 auto;
|
||||||
|
gap: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
margin-bottom: 72px;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-backup-overview-settings": HaBackupBackupsSummary;
|
||||||
|
}
|
||||||
|
}
|
@ -33,11 +33,11 @@ import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
|||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import { fileDownload } from "../../../../util/file_download";
|
import { fileDownload } from "../../../../util/file_download";
|
||||||
import { showToast } from "../../../../util/toast";
|
import { showToast } from "../../../../util/toast";
|
||||||
import "../components/ha-backup-config-agents";
|
import "../components/config/ha-backup-config-agents";
|
||||||
import "../components/ha-backup-config-data";
|
import "../components/config/ha-backup-config-data";
|
||||||
import type { BackupConfigData } from "../components/ha-backup-config-data";
|
import type { BackupConfigData } from "../components/config/ha-backup-config-data";
|
||||||
import "../components/ha-backup-config-schedule";
|
import "../components/config/ha-backup-config-schedule";
|
||||||
import type { BackupConfigSchedule } from "../components/ha-backup-config-schedule";
|
import type { BackupConfigSchedule } from "../components/config/ha-backup-config-schedule";
|
||||||
import type { BackupOnboardingDialogParams } from "./show-dialog-backup_onboarding";
|
import type { BackupOnboardingDialogParams } from "./show-dialog-backup_onboarding";
|
||||||
|
|
||||||
const STEPS = [
|
const STEPS = [
|
||||||
@ -367,8 +367,8 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
case "locations":
|
case "locations":
|
||||||
return html`
|
return html`
|
||||||
<p>
|
<p>
|
||||||
Home Assistant will upload to these locations when this backup
|
Home Assistant will upload to these locations when an automatic
|
||||||
settings are used. You can use all locations for custom backups.
|
backup is made. You can use all locations for manual backups.
|
||||||
</p>
|
</p>
|
||||||
<ha-backup-config-agents
|
<ha-backup-config-agents
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@ -2,6 +2,7 @@ import { mdiClose } from "@mdi/js";
|
|||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-dialog-header";
|
import "../../../../components/ha-dialog-header";
|
||||||
@ -28,8 +29,8 @@ import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
|||||||
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import "../components/ha-backup-agents-picker";
|
import "../components/ha-backup-agents-picker";
|
||||||
import "../components/ha-backup-config-data";
|
import "../components/config/ha-backup-config-data";
|
||||||
import type { BackupConfigData } from "../components/ha-backup-config-data";
|
import type { BackupConfigData } from "../components/config/ha-backup-config-data";
|
||||||
import type { GenerateBackupDialogParams } from "./show-dialog-generate-backup";
|
import type { GenerateBackupDialogParams } from "./show-dialog-generate-backup";
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
@ -278,11 +279,14 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
|
|||||||
include_homeassistant:
|
include_homeassistant:
|
||||||
data.include_homeassistant || data.include_database,
|
data.include_homeassistant || data.include_database,
|
||||||
include_database: data.include_database,
|
include_database: data.include_database,
|
||||||
include_addons: data.include_addons,
|
|
||||||
include_folders: data.include_folders,
|
|
||||||
include_all_addons: data.include_all_addons,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isComponentLoaded(this.hass, "hassio")) {
|
||||||
|
params.include_folders = data.include_folders;
|
||||||
|
params.include_all_addons = data.include_all_addons;
|
||||||
|
params.include_addons = data.include_addons;
|
||||||
|
}
|
||||||
|
|
||||||
this._params!.submit?.(params);
|
this._params!.submit?.(params);
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
@ -71,22 +71,22 @@ class DialogNewBackup extends LitElement implements HassDialog {
|
|||||||
dialogInitialFocus
|
dialogInitialFocus
|
||||||
>
|
>
|
||||||
<ha-md-list-item
|
<ha-md-list-item
|
||||||
@click=${this._default}
|
@click=${this._automatic}
|
||||||
type="button"
|
type="button"
|
||||||
.disabled=${!this._params.config.create_backup.password}
|
.disabled=${!this._params.config.create_backup.password}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
||||||
<span slot="headline">Use automatic backups</span>
|
<span slot="headline">Automatic backup</span>
|
||||||
<span slot="supporting-text">
|
<span slot="supporting-text">
|
||||||
Create a backup with the data and locations you have configured.
|
Create a backup with the data and locations you have configured.
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-md-list-item>
|
</ha-md-list-item>
|
||||||
<ha-md-list-item @click=${this._custom} type="button">
|
<ha-md-list-item @click=${this._manual} type="button">
|
||||||
<ha-svg-icon slot="start" .path=${mdiPencil}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiPencil}></ha-svg-icon>
|
||||||
<span slot="headline">Make custom backup</span>
|
<span slot="headline">Manual backup</span>
|
||||||
<span slot="supporting-text">
|
<span slot="supporting-text">
|
||||||
Select specific data and locations for a custom backup.
|
Select data and locations for a manual backup.
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-md-list-item>
|
</ha-md-list-item>
|
||||||
@ -96,12 +96,12 @@ class DialogNewBackup extends LitElement implements HassDialog {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _custom() {
|
private async _manual() {
|
||||||
this._params!.submit?.("manual");
|
this._params!.submit?.("manual");
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _default() {
|
private async _automatic() {
|
||||||
this._params!.submit?.("automatic");
|
this._params!.submit?.("automatic");
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import { css, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
|
||||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||||
import { storage } from "../../../common/decorators/storage";
|
import { storage } from "../../../common/decorators/storage";
|
||||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
@ -22,12 +21,12 @@ import { capitalizeFirstLetter } from "../../../common/string/capitalize-first-l
|
|||||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import type {
|
import type {
|
||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
|
DataTableRowData,
|
||||||
RowClickedEvent,
|
RowClickedEvent,
|
||||||
SelectionChangedEvent,
|
SelectionChangedEvent,
|
||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-card";
|
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
@ -68,24 +67,20 @@ import { brandsUrl } from "../../../util/brands-url";
|
|||||||
import { bytesToString } from "../../../util/bytes-to-string";
|
import { bytesToString } from "../../../util/bytes-to-string";
|
||||||
import { fileDownload } from "../../../util/file_download";
|
import { fileDownload } from "../../../util/file_download";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
import "./components/ha-backup-summary-card";
|
|
||||||
import "./components/ha-backup-summary-progress";
|
|
||||||
import "./components/ha-backup-summary-status";
|
|
||||||
import { showBackupOnboardingDialog } from "./dialogs/show-dialog-backup_onboarding";
|
|
||||||
import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup";
|
import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup";
|
||||||
import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup";
|
import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup";
|
||||||
import { showUploadBackupDialog } from "./dialogs/show-dialog-upload-backup";
|
import { showUploadBackupDialog } from "./dialogs/show-dialog-upload-backup";
|
||||||
|
|
||||||
interface BackupRow extends BackupContent {
|
interface BackupRow extends DataTableRowData, BackupContent {
|
||||||
formatted_type: string;
|
formatted_type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackupType = "automatic" | "manual";
|
type BackupType = "automatic" | "manual" | "imported";
|
||||||
|
|
||||||
const TYPE_ORDER: Array<BackupType> = ["automatic", "manual"];
|
const TYPE_ORDER: Array<BackupType> = ["automatic", "manual", "imported"];
|
||||||
|
|
||||||
@customElement("ha-config-backup-dashboard")
|
@customElement("ha-config-backup-backups")
|
||||||
class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public cloudStatus!: CloudStatus;
|
@property({ attribute: false }) public cloudStatus!: CloudStatus;
|
||||||
@ -98,8 +93,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _backups: BackupContent[] = [];
|
@state() private _backups: BackupContent[] = [];
|
||||||
|
|
||||||
@state() private _fetching = false;
|
|
||||||
|
|
||||||
@state() private _selected: string[] = [];
|
@state() private _selected: string[] = [];
|
||||||
|
|
||||||
@state() private _config?: BackupConfig;
|
@state() private _config?: BackupConfig;
|
||||||
@ -112,7 +105,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
state: false,
|
state: false,
|
||||||
subscribe: false,
|
subscribe: false,
|
||||||
})
|
})
|
||||||
private _activeCollapsed?: string;
|
private _activeCollapsed: string[] = [];
|
||||||
|
|
||||||
private _subscribed?: Promise<() => void>;
|
private _subscribed?: Promise<() => void>;
|
||||||
|
|
||||||
@ -271,7 +264,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
]}
|
]}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
back-path="/config/system"
|
back-path="/config/backup/overview"
|
||||||
clickable
|
clickable
|
||||||
id="backup_id"
|
id="backup_id"
|
||||||
selectable
|
selectable
|
||||||
@ -291,70 +284,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.backup.picker.search"
|
"ui.panel.config.backup.picker.search"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div slot="top-header" class="header">
|
|
||||||
${this._fetching
|
|
||||||
? html`
|
|
||||||
<ha-backup-summary-card
|
|
||||||
heading="Loading backups"
|
|
||||||
description="Your backup information is being retrieved."
|
|
||||||
has-action
|
|
||||||
status="loading"
|
|
||||||
>
|
|
||||||
<ha-button
|
|
||||||
slot="action"
|
|
||||||
@click=${this._configureAutomaticBackups}
|
|
||||||
>
|
|
||||||
Configure
|
|
||||||
</ha-button>
|
|
||||||
</ha-backup-summary-card>
|
|
||||||
`
|
|
||||||
: backupInProgress
|
|
||||||
? html`
|
|
||||||
<ha-backup-summary-progress
|
|
||||||
.hass=${this.hass}
|
|
||||||
.manager=${this._manager}
|
|
||||||
has-action
|
|
||||||
>
|
|
||||||
<ha-button
|
|
||||||
slot="action"
|
|
||||||
@click=${this._configureAutomaticBackups}
|
|
||||||
>
|
|
||||||
Configure
|
|
||||||
</ha-button>
|
|
||||||
</ha-backup-summary-progress>
|
|
||||||
`
|
|
||||||
: this._needsOnboarding
|
|
||||||
? html`
|
|
||||||
<ha-backup-summary-card
|
|
||||||
heading="Configure automatic backups"
|
|
||||||
description="Have a one-click backup automation with selected data and locations."
|
|
||||||
has-action
|
|
||||||
status="info"
|
|
||||||
>
|
|
||||||
<ha-button
|
|
||||||
slot="action"
|
|
||||||
@click=${this._setupAutomaticBackups}
|
|
||||||
>
|
|
||||||
Set up automatic backups
|
|
||||||
</ha-button>
|
|
||||||
</ha-backup-summary-card>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<ha-backup-summary-status
|
|
||||||
.hass=${this.hass}
|
|
||||||
.backups=${this._backups}
|
|
||||||
has-action
|
|
||||||
>
|
|
||||||
<ha-button
|
|
||||||
slot="action"
|
|
||||||
@click=${this._configureAutomaticBackups}
|
|
||||||
>
|
|
||||||
Configure
|
|
||||||
</ha-button>
|
|
||||||
</ha-backup-summary-status>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div slot="toolbar-icon">
|
<div slot="toolbar-icon">
|
||||||
<ha-button-menu>
|
<ha-button-menu>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
@ -410,9 +339,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
<ha-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
?disabled=${backupInProgress}
|
?disabled=${backupInProgress}
|
||||||
.label=${this.hass.localize(
|
.label=${"Backup now"}
|
||||||
"ui.panel.config.backup.create_backup"
|
|
||||||
)}
|
|
||||||
extended
|
extended
|
||||||
@click=${this._newBackup}
|
@click=${this._newBackup}
|
||||||
>
|
>
|
||||||
@ -466,10 +393,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this._fetching = true;
|
this._fetchBackupInfo();
|
||||||
this._fetchBackupInfo().then(() => {
|
|
||||||
this._fetching = false;
|
|
||||||
});
|
|
||||||
this._subscribeEvents();
|
this._subscribeEvents();
|
||||||
this._fetchBackupConfig();
|
this._fetchBackupConfig();
|
||||||
}
|
}
|
||||||
@ -529,12 +453,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isComponentLoaded(this.hass, "hassio")) {
|
|
||||||
delete params.include_folders;
|
|
||||||
delete params.include_all_addons;
|
|
||||||
delete params.include_addons;
|
|
||||||
}
|
|
||||||
|
|
||||||
await generateBackup(this.hass, params);
|
await generateBackup(this.hass, params);
|
||||||
await this._fetchBackupInfo();
|
await this._fetchBackupInfo();
|
||||||
return;
|
return;
|
||||||
@ -602,23 +520,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
this._dataTable.clearSelection();
|
this._dataTable.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _configureAutomaticBackups() {
|
|
||||||
navigate("/config/backup/settings");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _setupAutomaticBackups() {
|
|
||||||
const success = await showBackupOnboardingDialog(this, {
|
|
||||||
cloudStatus: this.cloudStatus,
|
|
||||||
});
|
|
||||||
if (!success) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._fetchBackupConfig();
|
|
||||||
await generateBackupWithAutomaticSettings(this.hass);
|
|
||||||
await this._fetchBackupInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@ -688,6 +589,6 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-config-backup-dashboard": HaConfigBackupDashboard;
|
"ha-config-backup-backups": HaConfigBackupBackups;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -87,7 +87,7 @@ class HaConfigBackupDetails extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage
|
<hass-subpage
|
||||||
back-path="/config/backup"
|
back-path="/config/backup/backups"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
.header=${this._backup?.name || "Backup"}
|
.header=${this._backup?.name || "Backup"}
|
||||||
|
270
src/panels/config/backup/ha-config-backup-overview.ts
Normal file
270
src/panels/config/backup/ha-config-backup-overview.ts
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
import { mdiDotsVertical, mdiPlus, mdiUpload } from "@mdi/js";
|
||||||
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
||||||
|
import "../../../components/ha-button";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-fab";
|
||||||
|
import "../../../components/ha-icon";
|
||||||
|
import "../../../components/ha-icon-next";
|
||||||
|
import "../../../components/ha-icon-overflow-menu";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
|
import {
|
||||||
|
fetchBackupConfig,
|
||||||
|
fetchBackupInfo,
|
||||||
|
generateBackup,
|
||||||
|
generateBackupWithAutomaticSettings,
|
||||||
|
type BackupConfig,
|
||||||
|
type BackupContent,
|
||||||
|
} from "../../../data/backup";
|
||||||
|
import type { ManagerStateEvent } from "../../../data/backup_manager";
|
||||||
|
import { DEFAULT_MANAGER_STATE } from "../../../data/backup_manager";
|
||||||
|
import type { CloudStatus } from "../../../data/cloud";
|
||||||
|
import "../../../layouts/hass-subpage";
|
||||||
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
import type { HomeAssistant, Route } from "../../../types";
|
||||||
|
import "./components/ha-backup-summary-card";
|
||||||
|
import "./components/ha-backup-summary-progress";
|
||||||
|
import "./components/ha-backup-summary-status";
|
||||||
|
import "./components/overview/ha-backup-overview-backups";
|
||||||
|
import "./components/overview/ha-backup-overview-settings";
|
||||||
|
import { showBackupOnboardingDialog } from "./dialogs/show-dialog-backup_onboarding";
|
||||||
|
import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup";
|
||||||
|
import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup";
|
||||||
|
import { showUploadBackupDialog } from "./dialogs/show-dialog-upload-backup";
|
||||||
|
|
||||||
|
@customElement("ha-config-backup-overview")
|
||||||
|
class HaConfigBackupOverview extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public cloudStatus!: CloudStatus;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
|
@state() private _manager: ManagerStateEvent = DEFAULT_MANAGER_STATE;
|
||||||
|
|
||||||
|
@state() private _backups: BackupContent[] = [];
|
||||||
|
|
||||||
|
@state() private _fetching = false;
|
||||||
|
|
||||||
|
@state() private _config?: BackupConfig;
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._fetchBackupInfo();
|
||||||
|
this._fetchBackupConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
if (this.hasUpdated) {
|
||||||
|
this._fetchBackupInfo();
|
||||||
|
this._fetchBackupConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _uploadBackup(ev) {
|
||||||
|
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await showUploadBackupDialog(this, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _setupAutomaticBackup() {
|
||||||
|
const success = await showBackupOnboardingDialog(this, {
|
||||||
|
cloudStatus: this.cloudStatus,
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fetchBackupConfig();
|
||||||
|
await generateBackupWithAutomaticSettings(this.hass);
|
||||||
|
await this._fetchBackupInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchBackupInfo() {
|
||||||
|
const info = await fetchBackupInfo(this.hass);
|
||||||
|
this._backups = info.backups;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchBackupConfig() {
|
||||||
|
const { config } = await fetchBackupConfig(this.hass);
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _newBackup(): Promise<void> {
|
||||||
|
if (this._needsOnboarding) {
|
||||||
|
this._setupAutomaticBackup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = this._config;
|
||||||
|
|
||||||
|
const type = await showNewBackupDialog(this, { config });
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "manual") {
|
||||||
|
const params = await showGenerateBackupDialog(this, {});
|
||||||
|
|
||||||
|
if (!params) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await generateBackup(this.hass, params);
|
||||||
|
await this._fetchBackupInfo();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === "automatic") {
|
||||||
|
await generateBackupWithAutomaticSettings(this.hass);
|
||||||
|
await this._fetchBackupInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _needsOnboarding() {
|
||||||
|
return !this._config?.create_backup.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const backupInProgress =
|
||||||
|
"state" in this._manager && this._manager.state === "in_progress";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hass-subpage
|
||||||
|
back-path="/config/system"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.header=${"Backup"}
|
||||||
|
>
|
||||||
|
<div slot="toolbar-icon">
|
||||||
|
<ha-button-menu>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
></ha-icon-button>
|
||||||
|
<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
@request-selected=${this._uploadBackup}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="graphic" .path=${mdiUpload}></ha-svg-icon>
|
||||||
|
Upload backup
|
||||||
|
</ha-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
${this._fetching
|
||||||
|
? html`
|
||||||
|
<ha-backup-summary-card
|
||||||
|
heading="Loading backups"
|
||||||
|
description="Your backup information is being retrieved."
|
||||||
|
status="loading"
|
||||||
|
>
|
||||||
|
</ha-backup-summary-card>
|
||||||
|
`
|
||||||
|
: backupInProgress
|
||||||
|
? html`
|
||||||
|
<ha-backup-summary-progress
|
||||||
|
.hass=${this.hass}
|
||||||
|
.manager=${this._manager}
|
||||||
|
>
|
||||||
|
</ha-backup-summary-progress>
|
||||||
|
`
|
||||||
|
: this._needsOnboarding
|
||||||
|
? html`
|
||||||
|
<ha-backup-summary-card
|
||||||
|
heading="Configure automatic backups"
|
||||||
|
description="Have a one-click backup automation with selected data and locations."
|
||||||
|
has-action
|
||||||
|
status="info"
|
||||||
|
>
|
||||||
|
<ha-button
|
||||||
|
slot="action"
|
||||||
|
@click=${this._setupAutomaticBackup}
|
||||||
|
>
|
||||||
|
Set up automatic backups
|
||||||
|
</ha-button>
|
||||||
|
</ha-backup-summary-card>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-backup-summary-status
|
||||||
|
.hass=${this.hass}
|
||||||
|
.backups=${this._backups}
|
||||||
|
>
|
||||||
|
</ha-backup-summary-status>
|
||||||
|
`}
|
||||||
|
|
||||||
|
<ha-backup-overview-backups
|
||||||
|
.hass=${this.hass}
|
||||||
|
.backups=${this._backups}
|
||||||
|
></ha-backup-overview-backups>
|
||||||
|
|
||||||
|
${!this._needsOnboarding
|
||||||
|
? html`
|
||||||
|
<ha-backup-overview-settings
|
||||||
|
.hass=${this.hass}
|
||||||
|
.config=${this._config!}
|
||||||
|
></ha-backup-overview-settings>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ha-fab
|
||||||
|
slot="fab"
|
||||||
|
?disabled=${backupInProgress}
|
||||||
|
.label=${"Backup now"}
|
||||||
|
extended
|
||||||
|
@click=${this._newBackup}
|
||||||
|
>
|
||||||
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
|
</ha-fab>
|
||||||
|
</hass-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.content {
|
||||||
|
padding: 28px 20px 0;
|
||||||
|
max-width: 690px;
|
||||||
|
margin: 0 auto;
|
||||||
|
gap: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
margin-bottom: 72px;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-config-backup-overview": HaConfigBackupOverview;
|
||||||
|
}
|
||||||
|
}
|
@ -15,12 +15,12 @@ import {
|
|||||||
import type { CloudStatus } from "../../../data/cloud";
|
import type { CloudStatus } from "../../../data/cloud";
|
||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "./components/ha-backup-config-agents";
|
import "./components/config/ha-backup-config-agents";
|
||||||
import "./components/ha-backup-config-data";
|
import "./components/config/ha-backup-config-data";
|
||||||
import type { BackupConfigData } from "./components/ha-backup-config-data";
|
import type { BackupConfigData } from "./components/config/ha-backup-config-data";
|
||||||
import "./components/ha-backup-config-encryption-key";
|
import "./components/config/ha-backup-config-encryption-key";
|
||||||
import "./components/ha-backup-config-schedule";
|
import "./components/config/ha-backup-config-schedule";
|
||||||
import type { BackupConfigSchedule } from "./components/ha-backup-config-schedule";
|
import type { BackupConfigSchedule } from "./components/config/ha-backup-config-schedule";
|
||||||
|
|
||||||
const INITIAL_BACKUP_CONFIG: BackupConfig = {
|
const INITIAL_BACKUP_CONFIG: BackupConfig = {
|
||||||
create_backup: {
|
create_backup: {
|
||||||
|
@ -5,7 +5,8 @@ import type { RouterOptions } from "../../../layouts/hass-router-page";
|
|||||||
import { HassRouterPage } from "../../../layouts/hass-router-page";
|
import { HassRouterPage } from "../../../layouts/hass-router-page";
|
||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "./ha-config-backup-dashboard";
|
import "./ha-config-backup-overview";
|
||||||
|
import "./ha-config-backup-backups";
|
||||||
|
|
||||||
@customElement("ha-config-backup")
|
@customElement("ha-config-backup")
|
||||||
class HaConfigBackup extends HassRouterPage {
|
class HaConfigBackup extends HassRouterPage {
|
||||||
@ -16,10 +17,14 @@ class HaConfigBackup extends HassRouterPage {
|
|||||||
@property({ type: Boolean }) public narrow = false;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
protected routerOptions: RouterOptions = {
|
protected routerOptions: RouterOptions = {
|
||||||
defaultPage: "dashboard",
|
defaultPage: "overview",
|
||||||
routes: {
|
routes: {
|
||||||
dashboard: {
|
overview: {
|
||||||
tag: "ha-config-backup-dashboard",
|
tag: "ha-config-backup-overview",
|
||||||
|
cache: true,
|
||||||
|
},
|
||||||
|
backups: {
|
||||||
|
tag: "ha-config-backup-backups",
|
||||||
cache: true,
|
cache: true,
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user