mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-10 19:06:36 +00:00
Multiple backup adjustments (#23361)
* Always show welcome screen during onboarding * Fix content overflow in dialog * Rename copies to backups, drop 7 days * Remove including new and learn more button * Some other fixes * Allow changing default local location from new backup page * Dont show addon version in settings and onboarding * Add margin for ios * Display old encryption key before change * Refactor descriptipn
This commit is contained in:
parent
c66f5e2d8a
commit
fb228dc918
@ -1,5 +1,6 @@
|
|||||||
import { mdiBackupRestore, mdiFolder, mdiHarddisk, mdiPlayBox } from "@mdi/js";
|
import { mdiBackupRestore, mdiFolder, mdiHarddisk, mdiPlayBox } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import type { CSSResultGroup } 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";
|
||||||
@ -173,6 +174,16 @@ class HaMountPicker extends LitElement {
|
|||||||
fireEvent(this, "change");
|
fireEvent(this, "change");
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
css`
|
||||||
|
ha-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -68,7 +68,8 @@ export type Selector =
|
|||||||
| TTSVoiceSelector
|
| TTSVoiceSelector
|
||||||
| UiActionSelector
|
| UiActionSelector
|
||||||
| UiColorSelector
|
| UiColorSelector
|
||||||
| UiStateContentSelector;
|
| UiStateContentSelector
|
||||||
|
| BackupLocationSelector;
|
||||||
|
|
||||||
export interface ActionSelector {
|
export interface ActionSelector {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
@ -48,6 +48,13 @@ class HaBackupConfigAgents extends LitElement {
|
|||||||
return this.value ?? DEFAULT_AGENTS;
|
return this.value ?? DEFAULT_AGENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _description(agentId: string) {
|
||||||
|
if (agentId === CLOUD_AGENT) {
|
||||||
|
return "It stores one backup. The oldest backups are deleted.";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
${this._agentIds.length > 0
|
${this._agentIds.length > 0
|
||||||
@ -60,6 +67,7 @@ class HaBackupConfigAgents extends LitElement {
|
|||||||
agentId,
|
agentId,
|
||||||
this._agentIds
|
this._agentIds
|
||||||
);
|
);
|
||||||
|
const description = this._description(agentId);
|
||||||
return html`
|
return html`
|
||||||
<ha-md-list-item>
|
<ha-md-list-item>
|
||||||
${isLocalAgent(agentId)
|
${isLocalAgent(agentId)
|
||||||
@ -82,13 +90,8 @@ class HaBackupConfigAgents extends LitElement {
|
|||||||
/>
|
/>
|
||||||
`}
|
`}
|
||||||
<div slot="headline">${name}</div>
|
<div slot="headline">${name}</div>
|
||||||
${agentId === CLOUD_AGENT
|
${description
|
||||||
? html`
|
? html`<div slot="supporting-text">${description}</div>`
|
||||||
<div slot="supporting-text">
|
|
||||||
It stores one backup. The oldest backups are
|
|
||||||
deleted.
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-switch
|
<ha-switch
|
||||||
slot="end"
|
slot="end"
|
||||||
|
@ -60,6 +60,9 @@ class HaBackupConfigData extends LitElement {
|
|||||||
@property({ type: Boolean, attribute: "force-home-assistant" })
|
@property({ type: Boolean, attribute: "force-home-assistant" })
|
||||||
public forceHomeAssistant = false;
|
public forceHomeAssistant = false;
|
||||||
|
|
||||||
|
@property({ attribute: "hide-addon-version", type: Boolean })
|
||||||
|
public hideAddonVersion = false;
|
||||||
|
|
||||||
@state() private value?: BackupConfigData;
|
@state() private value?: BackupConfigData;
|
||||||
|
|
||||||
@state() private _addons: BackupAddonItem[] = [];
|
@state() private _addons: BackupAddonItem[] = [];
|
||||||
@ -156,7 +159,7 @@ class HaBackupConfigData extends LitElement {
|
|||||||
The bare minimum needed to restore your system.
|
The bare minimum needed to restore your system.
|
||||||
</span>
|
</span>
|
||||||
${this.forceHomeAssistant
|
${this.forceHomeAssistant
|
||||||
? html`<ha-button slot="end">Learn more</ha-button>`
|
? nothing
|
||||||
: html`
|
: html`
|
||||||
<ha-switch
|
<ha-switch
|
||||||
id="homeassistant"
|
id="homeassistant"
|
||||||
@ -253,7 +256,7 @@ class HaBackupConfigData extends LitElement {
|
|||||||
.value=${data.addons_mode}
|
.value=${data.addons_mode}
|
||||||
>
|
>
|
||||||
<ha-md-select-option value="all">
|
<ha-md-select-option value="all">
|
||||||
<div slot="headline">All, including new</div>
|
<div slot="headline">All</div>
|
||||||
</ha-md-select-option>
|
</ha-md-select-option>
|
||||||
<ha-md-select-option value="none">
|
<ha-md-select-option value="none">
|
||||||
<div slot="headline">None</div>
|
<div slot="headline">None</div>
|
||||||
@ -276,6 +279,7 @@ class HaBackupConfigData extends LitElement {
|
|||||||
.value=${data.addons}
|
.value=${data.addons}
|
||||||
@value-changed=${this._addonsChanged}
|
@value-changed=${this._addonsChanged}
|
||||||
.addons=${this._addons}
|
.addons=${this._addons}
|
||||||
|
.hideVersion=${this.hideAddonVersion}
|
||||||
></ha-backup-addons-picker>
|
></ha-backup-addons-picker>
|
||||||
</ha-expansion-panel>
|
</ha-expansion-panel>
|
||||||
`
|
`
|
||||||
|
@ -23,7 +23,6 @@ const MAX_VALUE = 50;
|
|||||||
|
|
||||||
enum RetentionPreset {
|
enum RetentionPreset {
|
||||||
COPIES_3 = "copies_3",
|
COPIES_3 = "copies_3",
|
||||||
DAYS_7 = "days_7",
|
|
||||||
FOREVER = "forever",
|
FOREVER = "forever",
|
||||||
CUSTOM = "custom",
|
CUSTOM = "custom",
|
||||||
}
|
}
|
||||||
@ -38,7 +37,6 @@ const RETENTION_PRESETS: Record<
|
|||||||
RetentionData
|
RetentionData
|
||||||
> = {
|
> = {
|
||||||
copies_3: { type: "copies", value: 3 },
|
copies_3: { type: "copies", value: 3 },
|
||||||
days_7: { type: "days", value: 7 },
|
|
||||||
forever: { type: "days", value: 0 },
|
forever: { type: "days", value: 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,7 +174,7 @@ class HaBackupConfigSchedule extends LitElement {
|
|||||||
</ha-md-select>
|
</ha-md-select>
|
||||||
</ha-md-list-item>
|
</ha-md-list-item>
|
||||||
<ha-md-list-item>
|
<ha-md-list-item>
|
||||||
<span slot="headline">Maximum copies</span>
|
<span slot="headline">Backups to keep</span>
|
||||||
<span slot="supporting-text">
|
<span slot="supporting-text">
|
||||||
The number of backups that are saved
|
The number of backups that are saved
|
||||||
</span>
|
</span>
|
||||||
@ -186,13 +184,10 @@ class HaBackupConfigSchedule extends LitElement {
|
|||||||
.value=${this._retentionPreset}
|
.value=${this._retentionPreset}
|
||||||
>
|
>
|
||||||
<ha-md-select-option .value=${RetentionPreset.COPIES_3}>
|
<ha-md-select-option .value=${RetentionPreset.COPIES_3}>
|
||||||
<div slot="headline">Latest 3 copies</div>
|
<div slot="headline">3 backups</div>
|
||||||
</ha-md-select-option>
|
|
||||||
<ha-md-select-option .value=${RetentionPreset.DAYS_7}>
|
|
||||||
<div slot="headline">Keep 7 days</div>
|
|
||||||
</ha-md-select-option>
|
</ha-md-select-option>
|
||||||
<ha-md-select-option .value=${RetentionPreset.FOREVER}>
|
<ha-md-select-option .value=${RetentionPreset.FOREVER}>
|
||||||
<div slot="headline">Keep forever</div>
|
<div slot="headline">All backups</div>
|
||||||
</ha-md-select-option>
|
</ha-md-select-option>
|
||||||
<ha-md-select-option .value=${RetentionPreset.CUSTOM}>
|
<ha-md-select-option .value=${RetentionPreset.CUSTOM}>
|
||||||
<div slot="headline">Custom</div>
|
<div slot="headline">Custom</div>
|
||||||
@ -223,7 +218,7 @@ class HaBackupConfigSchedule extends LitElement {
|
|||||||
<div slot="headline">days</div>
|
<div slot="headline">days</div>
|
||||||
</ha-md-select-option>
|
</ha-md-select-option>
|
||||||
<ha-md-select-option .value=${"copies"}>
|
<ha-md-select-option .value=${"copies"}>
|
||||||
<div slot="headline">copies</div>
|
<div slot="headline">backups</div>
|
||||||
</ha-md-select-option>
|
</ha-md-select-option>
|
||||||
</ha-md-select>
|
</ha-md-select>
|
||||||
</ha-md-list-item>
|
</ha-md-list-item>
|
||||||
|
@ -26,6 +26,9 @@ export class HaBackupAddonsPicker extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public value?: string[];
|
@property({ attribute: false }) public value?: string[];
|
||||||
|
|
||||||
|
@property({ attribute: "hide-version", type: Boolean })
|
||||||
|
public hideVersion = false;
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="items">
|
<div class="items">
|
||||||
@ -35,7 +38,7 @@ export class HaBackupAddonsPicker extends LitElement {
|
|||||||
<ha-backup-formfield-label
|
<ha-backup-formfield-label
|
||||||
slot="label"
|
slot="label"
|
||||||
.label=${item.name}
|
.label=${item.name}
|
||||||
.version=${item.version}
|
.version=${this.hideVersion ? undefined : item.version}
|
||||||
.iconPath=${item.iconPath || mdiPuzzle}
|
.iconPath=${item.iconPath || mdiPuzzle}
|
||||||
.imageUrl=${this.addons?.find((a) => a.slug === item.slug)?.icon
|
.imageUrl=${this.addons?.find((a) => a.slug === item.slug)?.icon
|
||||||
? `/api/hassio/addons/${item.slug}/icon`
|
? `/api/hassio/addons/${item.slug}/icon`
|
||||||
|
@ -43,7 +43,7 @@ class SupervisorFormfieldLabel extends LitElement {
|
|||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
margin-inline-end: 4px;
|
margin-inline-end: 4px;
|
||||||
margin-inline-start: initial;
|
margin-inline-start: initial;
|
||||||
font-size: 16px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
|
@ -102,8 +102,7 @@ class HaBackupOverviewBackups extends LitElement {
|
|||||||
gap: 24px;
|
gap: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-bottom: 24px;
|
margin-bottom: calc(72px + env(safe-area-inset-bottom));
|
||||||
margin-bottom: 72px;
|
|
||||||
}
|
}
|
||||||
.card-actions {
|
.card-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -38,7 +38,7 @@ class HaBackupOverviewBackups extends LitElement {
|
|||||||
Backups are essential to a reliable smart home. They protect your
|
Backups are essential to a reliable smart home. They protect your
|
||||||
setup against failures and allows you to quickly have a working
|
setup against failures and allows you to quickly have a working
|
||||||
system again. It is recommended to create a daily backup and keep
|
system again. It is recommended to create a daily backup and keep
|
||||||
copies of the last 3 days on two different locations. And one of
|
backups of the last 3 days on two different locations. And one of
|
||||||
them is off-site.
|
them is off-site.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,7 +33,7 @@ class HaBackupBackupsSummary extends LitElement {
|
|||||||
|
|
||||||
let copiesText = "and keep all backups";
|
let copiesText = "and keep all backups";
|
||||||
if (copies) {
|
if (copies) {
|
||||||
copiesText = `and keep the latest ${copies} copie(s)`;
|
copiesText = `and keep the latest ${copies} backup(s)`;
|
||||||
} else if (days) {
|
} else if (days) {
|
||||||
copiesText = `and keep backups for ${days} day(s)`;
|
copiesText = `and keep backups for ${days} day(s)`;
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ class HaBackupBackupsSummary extends LitElement {
|
|||||||
|
|
||||||
private _addonsDescription(config: BackupConfig): string {
|
private _addonsDescription(config: BackupConfig): string {
|
||||||
if (config.create_backup.include_all_addons) {
|
if (config.create_backup.include_all_addons) {
|
||||||
return "All add-ons, including new";
|
return "All add-ons";
|
||||||
}
|
}
|
||||||
if (config.create_backup.include_addons?.length) {
|
if (config.create_backup.include_addons?.length) {
|
||||||
return `${config.create_backup.include_addons.length} add-ons`;
|
return `${config.create_backup.include_addons.length} add-ons`;
|
||||||
|
@ -82,8 +82,6 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
|
|
||||||
@state() private _step?: Step;
|
@state() private _step?: Step;
|
||||||
|
|
||||||
@state() private _steps: Step[] = [];
|
|
||||||
|
|
||||||
@state() private _params?: BackupOnboardingDialogParams;
|
@state() private _params?: BackupOnboardingDialogParams;
|
||||||
|
|
||||||
@query("ha-md-dialog") private _dialog!: HaMdDialog;
|
@query("ha-md-dialog") private _dialog!: HaMdDialog;
|
||||||
@ -92,8 +90,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
|
|
||||||
public showDialog(params: BackupOnboardingDialogParams): void {
|
public showDialog(params: BackupOnboardingDialogParams): void {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
this._steps = params.showIntro ? STEPS.concat() : STEPS.slice(1);
|
this._step = STEPS[0];
|
||||||
this._step = this._steps[0];
|
|
||||||
this._config = RECOMMENDED_CONFIG;
|
this._config = RECOMMENDED_CONFIG;
|
||||||
|
|
||||||
const agents: string[] = [];
|
const agents: string[] = [];
|
||||||
@ -128,7 +125,6 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
}
|
}
|
||||||
this._opened = false;
|
this._opened = false;
|
||||||
this._step = undefined;
|
this._step = undefined;
|
||||||
this._steps = [];
|
|
||||||
this._config = undefined;
|
this._config = undefined;
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
}
|
}
|
||||||
@ -170,19 +166,19 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _previousStep() {
|
private _previousStep() {
|
||||||
const index = this._steps.indexOf(this._step!);
|
const index = STEPS.indexOf(this._step!);
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._step = this._steps[index - 1];
|
this._step = STEPS[index - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _nextStep() {
|
private _nextStep() {
|
||||||
const index = this._steps.indexOf(this._step!);
|
const index = STEPS.indexOf(this._step!);
|
||||||
if (index === this._steps.length - 1) {
|
if (index === STEPS.length - 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._step = this._steps[index + 1];
|
this._step = STEPS[index + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
@ -190,8 +186,8 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLastStep = this._step === this._steps[this._steps.length - 1];
|
const isLastStep = this._step === STEPS[STEPS.length - 1];
|
||||||
const isFirstStep = this._step === this._steps[0];
|
const isFirstStep = this._step === STEPS[0];
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-md-dialog disable-cancel-action open @closed=${this.closeDialog}>
|
<ha-md-dialog disable-cancel-action open @closed=${this.closeDialog}>
|
||||||
@ -224,7 +220,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
@click=${this._done}
|
@click=${this._done}
|
||||||
.disabled=${!this._isStepValid()}
|
.disabled=${!this._isStepValid()}
|
||||||
>
|
>
|
||||||
Save
|
Save and create backup
|
||||||
</ha-button>
|
</ha-button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
@ -296,7 +292,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
Backups are essential to a reliable smart home. They protect your
|
Backups are essential to a reliable smart home. They protect your
|
||||||
setup against failures and allows you to quickly have a working
|
setup against failures and allows you to quickly have a working
|
||||||
system again. It is recommended to create a daily backup and keep
|
system again. It is recommended to create a daily backup and keep
|
||||||
copies of the last 3 days on two different locations. And one of
|
backups of the last 3 days on two different locations. And one of
|
||||||
them is off-site.
|
them is off-site.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -331,7 +327,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
case "setup":
|
case "setup":
|
||||||
return html`
|
return html`
|
||||||
<p>
|
<p>
|
||||||
It is recommended to create a daily backup and keep copies of the
|
It is recommended to create a daily backup and keep backups of the
|
||||||
last 3 days on two different locations. And one of them is off-site.
|
last 3 days on two different locations. And one of them is off-site.
|
||||||
</p>
|
</p>
|
||||||
<ha-md-list class="full">
|
<ha-md-list class="full">
|
||||||
@ -355,7 +351,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
return html`
|
return html`
|
||||||
<p>
|
<p>
|
||||||
Let Home Assistant take care of your backups by creating a scheduled
|
Let Home Assistant take care of your backups by creating a scheduled
|
||||||
backup that also removes older copies.
|
backup that also removes older backups.
|
||||||
</p>
|
</p>
|
||||||
<ha-backup-config-schedule
|
<ha-backup-config-schedule
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -374,6 +370,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
.value=${this._dataConfig(this._config)}
|
.value=${this._dataConfig(this._config)}
|
||||||
@value-changed=${this._dataChanged}
|
@value-changed=${this._dataChanged}
|
||||||
force-home-assistant
|
force-home-assistant
|
||||||
|
hide-addon-version
|
||||||
></ha-backup-config-data>
|
></ha-backup-config-data>
|
||||||
`;
|
`;
|
||||||
case "locations":
|
case "locations":
|
||||||
@ -470,9 +467,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
|||||||
ha-md-dialog {
|
ha-md-dialog {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
max-width: 560px;
|
max-width: 560px;
|
||||||
}
|
--dialog-content-padding: 8px 24px;
|
||||||
div[slot="content"] {
|
|
||||||
margin-top: -16px;
|
|
||||||
}
|
}
|
||||||
ha-md-list {
|
ha-md-list {
|
||||||
background: none;
|
background: none;
|
||||||
|
@ -141,6 +141,13 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
|||||||
have access to all your current backups. All next backups will use
|
have access to all your current backups. All next backups will use
|
||||||
the new encryption key.
|
the new encryption key.
|
||||||
</p>
|
</p>
|
||||||
|
<div class="encryption-key">
|
||||||
|
<p>${this._params?.currentKey}</p>
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${mdiContentCopy}
|
||||||
|
@click=${this._copyOldKeyToClipboard}
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
<ha-md-list>
|
<ha-md-list>
|
||||||
<ha-md-list-item>
|
<ha-md-list-item>
|
||||||
<span slot="headline">Download old emergency kit</span>
|
<span slot="headline">Download old emergency kit</span>
|
||||||
@ -202,6 +209,16 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _copyOldKeyToClipboard() {
|
||||||
|
if (!this._params?.currentKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
copyToClipboard(this._params.currentKey);
|
||||||
|
showToast(this, {
|
||||||
|
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _downloadOld() {
|
private _downloadOld() {
|
||||||
if (!this._params?.currentKey) {
|
if (!this._params?.currentKey) {
|
||||||
return;
|
return;
|
||||||
@ -240,9 +257,7 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
|||||||
ha-md-dialog {
|
ha-md-dialog {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
max-width: 560px;
|
max-width: 560px;
|
||||||
}
|
--dialog-content-padding: 8px 24px;
|
||||||
div[slot="content"] {
|
|
||||||
margin-top: -16px;
|
|
||||||
}
|
}
|
||||||
ha-md-list {
|
ha-md-list {
|
||||||
background: none;
|
background: none;
|
||||||
|
151
src/panels/config/backup/dialogs/dialog-local-backup-location.ts
Normal file
151
src/panels/config/backup/dialogs/dialog-local-backup-location.ts
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import type { CSSResultGroup } from "lit";
|
||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-button";
|
||||||
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
|
import "../../../../components/ha-form/ha-form";
|
||||||
|
import type {
|
||||||
|
HaFormSchema,
|
||||||
|
SchemaUnion,
|
||||||
|
} from "../../../../components/ha-form/types";
|
||||||
|
import { extractApiErrorMessage } from "../../../../data/hassio/common";
|
||||||
|
import { changeMountOptions } from "../../../../data/supervisor/mounts";
|
||||||
|
import { haStyle, haStyleDialog } from "../../../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import type { LocalBackupLocationDialogParams } from "./show-dialog-local-backup-location";
|
||||||
|
|
||||||
|
const SCHEMA = [
|
||||||
|
{
|
||||||
|
name: "default_backup_mount",
|
||||||
|
required: true,
|
||||||
|
selector: { backup_location: {} },
|
||||||
|
},
|
||||||
|
] as const satisfies HaFormSchema[];
|
||||||
|
|
||||||
|
@customElement("dialog-local-backup-location")
|
||||||
|
class LocalBackupLocationDialog extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _dialogParams?: LocalBackupLocationDialogParams;
|
||||||
|
|
||||||
|
@state() private _data?: { default_backup_mount: string | null };
|
||||||
|
|
||||||
|
@state() private _waiting?: boolean;
|
||||||
|
|
||||||
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
public async showDialog(
|
||||||
|
dialogParams: LocalBackupLocationDialogParams
|
||||||
|
): Promise<void> {
|
||||||
|
this._dialogParams = dialogParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._data = undefined;
|
||||||
|
this._error = undefined;
|
||||||
|
this._waiting = undefined;
|
||||||
|
this._dialogParams = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._dialogParams) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
scrimClickAction
|
||||||
|
escapeKeyAction
|
||||||
|
.heading=${createCloseHeading(
|
||||||
|
this.hass,
|
||||||
|
this.hass.localize(
|
||||||
|
`ui.panel.config.backup.dialogs.local_backup_location.title`
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
@closed=${this.closeDialog}
|
||||||
|
>
|
||||||
|
${this._error
|
||||||
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
|
: nothing}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.backup.dialogs.local_backup_location.description`
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<ha-form
|
||||||
|
.hass=${this.hass}
|
||||||
|
.data=${this._data}
|
||||||
|
.schema=${SCHEMA}
|
||||||
|
.computeLabel=${this._computeLabelCallback}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
dialogInitialFocus
|
||||||
|
></ha-form>
|
||||||
|
<ha-button
|
||||||
|
slot="secondaryAction"
|
||||||
|
@click=${this.closeDialog}
|
||||||
|
dialogInitialFocus
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.common.cancel")}
|
||||||
|
</ha-button>
|
||||||
|
<ha-button
|
||||||
|
.disabled=${this._waiting || !this._data}
|
||||||
|
slot="primaryAction"
|
||||||
|
@click=${this._changeMount}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.common.save")}
|
||||||
|
</ha-button>
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeLabelCallback = (
|
||||||
|
schema: SchemaUnion<typeof SCHEMA>
|
||||||
|
): string =>
|
||||||
|
this.hass.localize(
|
||||||
|
`ui.panel.config.backup.dialogs.local_backup_location.options.${schema.name}.name`
|
||||||
|
) || schema.name;
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent) {
|
||||||
|
const newLocation = ev.detail.value.default_backup_mount;
|
||||||
|
this._data = {
|
||||||
|
default_backup_mount: newLocation === "/backup" ? null : newLocation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _changeMount() {
|
||||||
|
if (!this._data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._error = undefined;
|
||||||
|
this._waiting = true;
|
||||||
|
try {
|
||||||
|
await changeMountOptions(this.hass, this._data);
|
||||||
|
} catch (err: any) {
|
||||||
|
this._error = extractApiErrorMessage(err);
|
||||||
|
this._waiting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
ha-dialog {
|
||||||
|
--mdc-dialog-max-width: 500px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-local-backup-location": LocalBackupLocationDialog;
|
||||||
|
}
|
||||||
|
}
|
@ -115,9 +115,6 @@ class DialogNewBackup extends LitElement implements HassDialog {
|
|||||||
--dialog-content-padding: 0;
|
--dialog-content-padding: 0;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
div[slot="content"] {
|
|
||||||
margin-top: -16px;
|
|
||||||
}
|
|
||||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
ha-md-dialog {
|
ha-md-dialog {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
@ -157,7 +157,11 @@ class DialogRestoreBackupEncryptionKey
|
|||||||
</div>
|
</div>
|
||||||
<div slot="actions">
|
<div slot="actions">
|
||||||
<ha-button @click=${this.closeDialog}>Cancel</ha-button>
|
<ha-button @click=${this.closeDialog}>Cancel</ha-button>
|
||||||
<ha-button @click=${this._submit} .disabled=${!this._getKey()}>
|
<ha-button
|
||||||
|
@click=${this._submit}
|
||||||
|
class="danger"
|
||||||
|
.disabled=${!this._getKey()}
|
||||||
|
>
|
||||||
Restore
|
Restore
|
||||||
</ha-button>
|
</ha-button>
|
||||||
</div>
|
</div>
|
||||||
@ -221,10 +225,14 @@ class DialogRestoreBackupEncryptionKey
|
|||||||
ha-md-dialog {
|
ha-md-dialog {
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
--dialog-content-padding: 8px 24px;
|
||||||
}
|
}
|
||||||
.content p {
|
.content p {
|
||||||
margin: 0 0 16px;
|
margin: 0 0 16px;
|
||||||
}
|
}
|
||||||
|
ha-button.danger {
|
||||||
|
--mdc-theme-primary: var(--error-color);
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -193,9 +193,7 @@ class DialogSetBackupEncryptionKey extends LitElement implements HassDialog {
|
|||||||
ha-md-dialog {
|
ha-md-dialog {
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
}
|
--dialog-content-padding: 8px 24px;
|
||||||
div[slot="content"] {
|
|
||||||
margin-top: -16px;
|
|
||||||
}
|
}
|
||||||
ha-md-list {
|
ha-md-list {
|
||||||
background: none;
|
background: none;
|
||||||
|
@ -4,7 +4,6 @@ import type { CloudStatus } from "../../../../data/cloud";
|
|||||||
export interface BackupOnboardingDialogParams {
|
export interface BackupOnboardingDialogParams {
|
||||||
submit?: (value: boolean) => void;
|
submit?: (value: boolean) => void;
|
||||||
cancel?: () => void;
|
cancel?: () => void;
|
||||||
showIntro?: boolean;
|
|
||||||
cloudStatus: CloudStatus;
|
cloudStatus: CloudStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
export interface LocalBackupLocationDialogParams {}
|
||||||
|
|
||||||
|
export const showLocalBackupLocationDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
dialogParams: LocalBackupLocationDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-local-backup-location",
|
||||||
|
dialogImport: () => import("./dialog-local-backup-location"),
|
||||||
|
dialogParams,
|
||||||
|
});
|
||||||
|
};
|
@ -119,26 +119,6 @@ class HaConfigBackupDetails extends LitElement {
|
|||||||
: !this._backup
|
: !this._backup
|
||||||
? html`<ha-circular-progress active></ha-circular-progress>`
|
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||||
: html`
|
: html`
|
||||||
<ha-card header="Select what to restore">
|
|
||||||
<div class="card-content">
|
|
||||||
<ha-backup-data-picker
|
|
||||||
.hass=${this.hass}
|
|
||||||
.data=${this._backup}
|
|
||||||
.value=${this._selectedBackup}
|
|
||||||
@value-changed=${this._selectedBackupChanged}
|
|
||||||
.addonsInfo=${this._addonsInfo}
|
|
||||||
>
|
|
||||||
</ha-backup-data-picker>
|
|
||||||
</div>
|
|
||||||
<div class="card-actions">
|
|
||||||
<ha-button
|
|
||||||
@click=${this._restore}
|
|
||||||
.disabled=${this._isRestoreDisabled()}
|
|
||||||
>
|
|
||||||
Restore
|
|
||||||
</ha-button>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
<ha-card header="Backup">
|
<ha-card header="Backup">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-md-list>
|
<ha-md-list>
|
||||||
@ -159,6 +139,27 @@ class HaConfigBackupDetails extends LitElement {
|
|||||||
</ha-md-list>
|
</ha-md-list>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
<ha-card header="Select what to restore">
|
||||||
|
<div class="card-content">
|
||||||
|
<ha-backup-data-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.data=${this._backup}
|
||||||
|
.value=${this._selectedBackup}
|
||||||
|
@value-changed=${this._selectedBackupChanged}
|
||||||
|
.addonsInfo=${this._addonsInfo}
|
||||||
|
>
|
||||||
|
</ha-backup-data-picker>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions">
|
||||||
|
<ha-button
|
||||||
|
@click=${this._restore}
|
||||||
|
.disabled=${this._isRestoreDisabled()}
|
||||||
|
class="danger"
|
||||||
|
>
|
||||||
|
Restore
|
||||||
|
</ha-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
<ha-card header="Locations">
|
<ha-card header="Locations">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-md-list>
|
<ha-md-list>
|
||||||
@ -172,6 +173,8 @@ class HaConfigBackupDetails extends LitElement {
|
|||||||
this._backup!.agent_ids!
|
this._backup!.agent_ids!
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isLocal = isLocalAgent(agentId);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-md-list-item>
|
<ha-md-list-item>
|
||||||
${isLocalAgent(agentId)
|
${isLocalAgent(agentId)
|
||||||
@ -204,7 +207,11 @@ class HaConfigBackupDetails extends LitElement {
|
|||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
${success ? "Backup synced" : "Backup failed"}
|
${success
|
||||||
|
? isLocal
|
||||||
|
? "Backup created"
|
||||||
|
: "Backup uploaded"
|
||||||
|
: "Backup failed"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
${success
|
${success
|
||||||
@ -390,6 +397,9 @@ class HaConfigBackupDetails extends LitElement {
|
|||||||
.warning ha-svg-icon {
|
.warning ha-svg-icon {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
|
ha-button.danger {
|
||||||
|
--mdc-theme-primary: var(--error-color);
|
||||||
|
}
|
||||||
ha-backup-data-picker {
|
ha-backup-data-picker {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { mdiDotsVertical, mdiPlus, mdiUpload } from "@mdi/js";
|
import { mdiDotsVertical, mdiHarddisk, mdiPlus, mdiUpload } from "@mdi/js";
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } 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 { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
@ -34,6 +35,7 @@ import "./components/overview/ha-backup-overview-settings";
|
|||||||
import "./components/overview/ha-backup-overview-summary";
|
import "./components/overview/ha-backup-overview-summary";
|
||||||
import { showBackupOnboardingDialog } from "./dialogs/show-dialog-backup_onboarding";
|
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 { showLocalBackupLocationDialog } from "./dialogs/show-dialog-local-backup-location";
|
||||||
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";
|
||||||
|
|
||||||
@ -63,15 +65,22 @@ class HaConfigBackupOverview extends LitElement {
|
|||||||
await showUploadBackupDialog(this, {});
|
await showUploadBackupDialog(this, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleOnboardingButtonClick(ev) {
|
private async _changeLocalLocation(ev) {
|
||||||
ev.stopPropagation();
|
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||||
this._setupAutomaticBackup(false);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _setupAutomaticBackup(showIntro: boolean) {
|
showLocalBackupLocationDialog(this, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleOnboardingButtonClick(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._setupAutomaticBackup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _setupAutomaticBackup() {
|
||||||
const success = await showBackupOnboardingDialog(this, {
|
const success = await showBackupOnboardingDialog(this, {
|
||||||
cloudStatus: this.cloudStatus,
|
cloudStatus: this.cloudStatus,
|
||||||
showIntro: showIntro,
|
|
||||||
});
|
});
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
@ -84,7 +93,7 @@ class HaConfigBackupOverview extends LitElement {
|
|||||||
|
|
||||||
private async _newBackup(): Promise<void> {
|
private async _newBackup(): Promise<void> {
|
||||||
if (this._needsOnboarding) {
|
if (this._needsOnboarding) {
|
||||||
this._setupAutomaticBackup(true);
|
this._setupAutomaticBackup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +134,8 @@ class HaConfigBackupOverview extends LitElement {
|
|||||||
const backupInProgress =
|
const backupInProgress =
|
||||||
"state" in this.manager && this.manager.state === "in_progress";
|
"state" in this.manager && this.manager.state === "in_progress";
|
||||||
|
|
||||||
|
const isHassio = isComponentLoaded(this.hass, "hassio");
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage
|
<hass-subpage
|
||||||
back-path="/config/system"
|
back-path="/config/system"
|
||||||
@ -139,6 +150,18 @@ class HaConfigBackupOverview extends LitElement {
|
|||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
${isHassio
|
||||||
|
? html`<ha-list-item
|
||||||
|
graphic="icon"
|
||||||
|
@request-selected=${this._changeLocalLocation}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiHarddisk}
|
||||||
|
></ha-svg-icon>
|
||||||
|
Change local location
|
||||||
|
</ha-list-item>`
|
||||||
|
: nothing}
|
||||||
<ha-list-item
|
<ha-list-item
|
||||||
graphic="icon"
|
graphic="icon"
|
||||||
@request-selected=${this._uploadBackup}
|
@request-selected=${this._uploadBackup}
|
||||||
|
@ -56,7 +56,7 @@ class HaConfigBackupSettings extends LitElement {
|
|||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>
|
<p>
|
||||||
Let Home Assistant take care of your backups by creating a
|
Let Home Assistant take care of your backups by creating a
|
||||||
scheduled backup that also removes older copies.
|
scheduled backup that also removes older backups.
|
||||||
</p>
|
</p>
|
||||||
<ha-backup-config-schedule
|
<ha-backup-config-schedule
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -73,6 +73,7 @@ class HaConfigBackupSettings extends LitElement {
|
|||||||
.value=${this._dataConfig}
|
.value=${this._dataConfig}
|
||||||
@value-changed=${this._dataConfigChanged}
|
@value-changed=${this._dataConfigChanged}
|
||||||
force-home-assistant
|
force-home-assistant
|
||||||
|
hide-addon-version
|
||||||
></ha-backup-config-data>
|
></ha-backup-config-data>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
@ -2209,6 +2209,17 @@
|
|||||||
},
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"search": "Search backups"
|
"search": "Search backups"
|
||||||
|
},
|
||||||
|
"dialogs": {
|
||||||
|
"local_backup_location": {
|
||||||
|
"title": "Change local backup location",
|
||||||
|
"description": "Change the default location where local backups are stored on your Home Assistant instance.",
|
||||||
|
"options": {
|
||||||
|
"default_backup_mount": {
|
||||||
|
"name": "Default location"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tag": {
|
"tag": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user