Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to change encryption for each backup location #23861

Merged
merged 17 commits into from
Jan 29, 2025
20 changes: 17 additions & 3 deletions src/data/backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface BackupConfig {
time?: string | null;
days: BackupDay[];
};
agents: BackupAgentsConfig;
}

export interface BackupMutableConfig {
Expand All @@ -78,20 +79,30 @@ export interface BackupMutableConfig {
time?: string | null;
days?: BackupDay[] | null;
};
agents?: BackupAgentsConfig;
}

export type BackupAgentsConfig = Record<string, BackupAgentConfig>;

export interface BackupAgentConfig {
protected: boolean;
}

export interface BackupAgent {
agent_id: string;
name: string;
}

export interface BackupContentAgent {
size: number;
protected: boolean;
}

export interface BackupContent {
backup_id: string;
date: string;
name: string;
protected: boolean;
size: number;
agent_ids?: string[];
agents: Record<string, BackupContentAgent>;
failed_agent_ids?: string[];
with_automatic_settings: boolean;
}
Expand Down Expand Up @@ -305,6 +316,9 @@ export const computeBackupAgentName = (
return showName ? `${domainName}: ${name}` : domainName;
};

export const computeBackupSize = (backup: BackupContent) =>
Math.max(...Object.values(backup.agents).map((agent) => agent.size));

export const compareAgents = (a: string, b: string) => {
const isLocalA = isLocalAgent(a);
const isLocalB = isLocalAgent(b);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { mdiHarddisk, mdiNas } from "@mdi/js";
import { mdiCog, mdiHarddisk, mdiNas } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeDomain } from "../../../../../common/entity/compute_domain";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/ha-switch";
import type { BackupAgent } from "../../../../../data/backup";
import type {
BackupAgent,
BackupAgentsConfig,
} from "../../../../../data/backup";
import {
CLOUD_AGENT,
computeBackupAgentName,
Expand All @@ -18,6 +22,7 @@ import {
import type { CloudStatus } from "../../../../../data/cloud";
import type { HomeAssistant } from "../../../../../types";
import { brandsUrl } from "../../../../../util/brands-url";
import { navigate } from "../../../../../common/navigate";

const DEFAULT_AGENTS = [];

Expand All @@ -29,6 +34,11 @@ class HaBackupConfigAgents extends LitElement {

@property({ attribute: false }) public agents: BackupAgent[] = [];

@property({ attribute: false }) public agentsConfig?: BackupAgentsConfig;

@property({ type: Boolean, attribute: "show-settings" }) public showSettings =
false;

bramkragten marked this conversation as resolved.
Show resolved Hide resolved
@state() private value?: string[];

private _availableAgents = memoizeOne(
Expand All @@ -53,6 +63,21 @@ class HaBackupConfigAgents extends LitElement {
"ui.panel.config.backup.agents.cloud_agent_description"
);
}

const encryptionTurnedOff =
this.agentsConfig?.[agentId]?.protected === false;

if (encryptionTurnedOff) {
return html`
<span class="dot warning"></span>
<span>
${this.hass.localize(
"ui.panel.config.backup.agents.encryption_turned_off"
)}
</span>
`;
}

if (isNetworkMountAgent(agentId)) {
return this.hass.localize(
"ui.panel.config.backup.agents.network_mount_agent_description"
Expand Down Expand Up @@ -80,6 +105,7 @@ class HaBackupConfigAgents extends LitElement {
agentId === CLOUD_AGENT &&
this.cloudStatus.logged_in &&
!this.cloudStatus.active_subscription;

return html`
<ha-md-list-item>
${isLocalAgent(agentId)
Expand Down Expand Up @@ -112,6 +138,16 @@ class HaBackupConfigAgents extends LitElement {
${description
? html`<div slot="supporting-text">${description}</div>`
: nothing}
${this.showSettings
? html`
<ha-icon-button
id=${agentId}
slot="end"
path=${mdiCog}
@click=${this._showAgentSettings}
></ha-icon-button>
`
: nothing}
<ha-switch
slot="end"
id=${agentId}
Expand All @@ -133,6 +169,11 @@ class HaBackupConfigAgents extends LitElement {
`;
}

private _showAgentSettings(ev): void {
const agentId = ev.currentTarget.id;
navigate(`/config/backup/location/${agentId}`);
}

private _agentToggled(ev) {
ev.stopPropagation();
const value = ev.currentTarget.checked;
Expand Down Expand Up @@ -180,6 +221,25 @@ class HaBackupConfigAgents extends LitElement {
--mdc-icon-size: 48px;
color: var(--primary-text-color);
}
ha-md-list-item [slot="supporting-text"] {
display: flex;
align-items: center;
flex-direction: row;
gap: 8px;
line-height: normal;
}
.dot {
display: block;
position: relative;
width: 8px;
height: 8px;
background-color: var(--disabled-color);
border-radius: 50%;
flex: none;
}
.dot.warning {
background-color: var(--warning-color);
}
`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ 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 {
computeBackupSize,
type BackupContent,
} from "../../../../../data/backup";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import { bytesToString } from "../../../../../util/bytes-to-string";
Expand All @@ -22,7 +25,7 @@ const computeBackupStats = (backups: BackupContent[]): BackupStats =>
backups.reduce(
(stats, backup) => {
stats.count++;
stats.size += backup.size;
stats.size += computeBackupSize(backup);
return stats;
},
{ count: 0, size: 0 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ class HaBackupOverviewBackups extends LitElement {
now,
true
),
count: lastUploadedBackup.agent_ids?.length ?? 0,
count: Object.keys(lastUploadedBackup.agents)
.length,
}
)
: nextBackupDescription}
Expand Down Expand Up @@ -265,7 +266,8 @@ class HaBackupOverviewBackups extends LitElement {
now,
true
),
count: lastUploadedBackup.agent_ids?.length ?? 0,
count: Object.keys(lastUploadedBackup.agents)
.length,
}
)
: nextBackupDescription}
Expand All @@ -286,7 +288,7 @@ class HaBackupOverviewBackups extends LitElement {
now,
true
),
count: lastBackup.agent_ids?.length ?? 0,
count: Object.keys(lastBackup.agents).length,
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const RECOMMENDED_CONFIG: BackupConfig = {
time: null,
days: [],
},
agents: {},
last_attempted_automatic_backup: null,
last_completed_automatic_backup: null,
next_automatic_backup: null,
Expand Down
12 changes: 8 additions & 4 deletions src/panels/config/backup/dialogs/dialog-restore-backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ class DialogRestoreBackup extends LitElement implements HassDialog {
this._error = undefined;
this._state = undefined;
this._stage = undefined;
if (this._params.backup.protected) {

const agentIds = Object.keys(this._params.backup.agents);
const preferedAgent = getPreferredAgentForDownload(agentIds);
bramkragten marked this conversation as resolved.
Show resolved Hide resolved
const isProtected = this._params.backup.agents[preferedAgent]?.protected;

if (isProtected) {
this._backupEncryptionKey = await this._fetchEncryptionKey();
if (!this._backupEncryptionKey) {
this._step = STEPS[1];
Expand Down Expand Up @@ -322,9 +327,8 @@ class DialogRestoreBackup extends LitElement implements HassDialog {
return;
}

const preferedAgent = getPreferredAgentForDownload(
this._params.backup.agent_ids!
);
const agentIds = Object.keys(this._params.backup.agents);
const preferedAgent = getPreferredAgentForDownload(agentIds);

const { addons, database_included, homeassistant_included, folders } =
this._params.selectedData;
Expand Down
6 changes: 6 additions & 0 deletions src/panels/config/backup/ha-config-backup-backups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ import type {
BackupContent,
} from "../../../data/backup";
import {
compareAgents,
computeBackupAgentName,
computeBackupSize,
deleteBackup,
generateBackup,
generateBackupWithAutomaticSettings,
Expand Down Expand Up @@ -68,6 +70,8 @@ import { downloadBackup } from "./helper/download_backup";

interface BackupRow extends DataTableRowData, BackupContent {
formatted_type: string;
size: number;
agent_ids: string[];
}

type BackupType = "automatic" | "manual";
Expand Down Expand Up @@ -291,6 +295,8 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
const type = backup.with_automatic_settings ? "automatic" : "manual";
return {
...backup,
size: computeBackupSize(backup),
agent_ids: Object.keys(backup.agents).sort(compareAgents),
formatted_type: localize(`ui.panel.config.backup.type.${type}`),
};
});
Expand Down
Loading
Loading