Use name property when formatting backup location (#23916)

This commit is contained in:
Paul Bottein 2025-01-28 13:53:02 +01:00 committed by GitHub
parent 142faba04d
commit 85e4975206
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 116 additions and 65 deletions

View File

@ -82,6 +82,7 @@ export interface BackupMutableConfig {
export interface BackupAgent {
agent_id: string;
name: string;
}
export interface BackupContent {
@ -279,13 +280,18 @@ export const isNetworkMountAgent = (agentId: string) => {
export const computeBackupAgentName = (
localize: LocalizeFunc,
agentId: string,
agentIds?: string[]
agents: BackupAgent[]
) => {
if (isLocalAgent(agentId)) {
return localize("ui.panel.config.backup.agents.local_agent");
}
const [domain, name] = agentId.split(".");
const agent = agents.find((a) => a.agent_id === agentId);
const domain = agentId.split(".")[0];
const name = agent ? agent.name : agentId.split(".")[1];
// If it's a network mount agent, only show the name
if (isNetworkMountAgent(agentId)) {
return name;
}
@ -293,9 +299,8 @@ export const computeBackupAgentName = (
const domainName = domainToName(localize, domain);
// If there are multiple agents for a domain, show the name
const showName = agentIds
? agentIds.filter((a) => a.split(".")[0] === domain).length > 1
: true;
const showName =
agents.filter((a) => a.agent_id.split(".")[0] === domain).length > 1;
return showName ? `${domainName}: ${name}` : domainName;
};

View File

@ -1,18 +1,17 @@
import { mdiHarddisk, mdiNas } from "@mdi/js";
import type { PropertyValues } from "lit";
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-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/ha-switch";
import type { BackupAgent } from "../../../../../data/backup";
import {
CLOUD_AGENT,
compareAgents,
computeBackupAgentName,
fetchBackupAgentsInfo,
isLocalAgent,
isNetworkMountAgent,
} from "../../../../../data/backup";
@ -28,22 +27,16 @@ class HaBackupConfigAgents extends LitElement {
@property({ attribute: false }) public cloudStatus!: CloudStatus;
@state() private _agentIds: string[] = [];
@property({ attribute: false }) public agents: BackupAgent[] = [];
@state() private value?: string[];
protected firstUpdated(_changedProperties: PropertyValues): void {
super.firstUpdated(_changedProperties);
this._fetchAgents();
}
private async _fetchAgents() {
const { agents } = await fetchBackupAgentsInfo(this.hass);
this._agentIds = agents
.map((agent) => agent.agent_id)
.filter((id) => id !== CLOUD_AGENT || this.cloudStatus.logged_in)
.sort(compareAgents);
}
private _availableAgents = memoizeOne(
(agents: BackupAgent[], cloudStatus: CloudStatus) =>
agents.filter(
(agent) => agent.agent_id !== CLOUD_AGENT || cloudStatus.logged_in
)
);
private get _value() {
return this.value ?? DEFAULT_AGENTS;
@ -69,16 +62,18 @@ class HaBackupConfigAgents extends LitElement {
}
protected render() {
const agents = this._availableAgents(this.agents, this.cloudStatus);
return html`
${this._agentIds.length > 0
${agents.length > 0
? html`
<ha-md-list>
${this._agentIds.map((agentId) => {
${agents.map((agent) => {
const agentId = agent.agent_id;
const domain = computeDomain(agentId);
const name = computeBackupAgentName(
this.hass.localize,
agentId,
this._agentIds
this.agents
);
const description = this._description(agentId);
const noCloudSubscription =
@ -130,9 +125,11 @@ class HaBackupConfigAgents extends LitElement {
})}
</ha-md-list>
`
: html`<p>
${this.hass.localize("ui.panel.config.backup.agents.no_agents")}
</p>`}
: html`
<p>
${this.hass.localize("ui.panel.config.backup.agents.no_agents")}
</p>
`}
`;
}
@ -147,9 +144,14 @@ class HaBackupConfigAgents extends LitElement {
this.value = this._value.filter((agent) => agent !== agentId);
}
const availableAgents = this._availableAgents(
this.agents,
this.cloudStatus
);
// Ensure we don't have duplicates, agents exist in the list and cloud is logged in
this.value = [...new Set(this.value)]
.filter((agent) => this._agentIds.some((id) => id === agent))
.filter((id) => availableAgents.some((agent) => agent.agent_id === id))
.filter(
(id) =>
id !== CLOUD_AGENT ||

View File

@ -7,6 +7,7 @@ import { computeDomain } from "../../../../common/entity/compute_domain";
import "../../../../components/ha-checkbox";
import "../../../../components/ha-formfield";
import "../../../../components/ha-svg-icon";
import type { BackupAgent } from "../../../../data/backup";
import {
computeBackupAgentName,
isLocalAgent,
@ -24,7 +25,7 @@ class HaBackupAgentsPicker extends LitElement {
public disabled = false;
@property({ attribute: false })
public agentIds!: string[];
public agents!: BackupAgent[];
@property({ attribute: false })
public disabledAgentIds?: string[];
@ -35,30 +36,30 @@ class HaBackupAgentsPicker extends LitElement {
render() {
return html`
<div class="agents">
${this.agentIds.map((agent) => this._renderAgent(agent))}
${this.agents.map((agent) => this._renderAgent(agent))}
</div>
`;
}
private _renderAgent(agentId: string) {
const domain = computeDomain(agentId);
private _renderAgent(agent: BackupAgent) {
const domain = computeDomain(agent.agent_id);
const name = computeBackupAgentName(
this.hass.localize,
agentId,
this.agentIds
agent.agent_id,
this.agents
);
const disabled =
this.disabled || this.disabledAgentIds?.includes(agentId) || false;
this.disabled || this.disabledAgentIds?.includes(agent.agent_id) || false;
return html`
<ha-formfield>
<span class="label ${classMap({ disabled })}" slot="label">
${isLocalAgent(agentId)
${isLocalAgent(agent.agent_id)
? html`
<ha-svg-icon .path=${mdiHarddisk} slot="start"> </ha-svg-icon>
`
: isNetworkMountAgent(agentId)
: isNetworkMountAgent(agent.agent_id)
? html` <ha-svg-icon .path=${mdiNas} slot="start"></ha-svg-icon> `
: html`
<img
@ -77,8 +78,8 @@ class HaBackupAgentsPicker extends LitElement {
${name}
</span>
<ha-checkbox
.checked=${this.value.includes(agentId)}
.value=${agentId}
.checked=${this.value.includes(agent.agent_id)}
.value=${agent.agent_id}
.disabled=${disabled}
@change=${this._checkboxChanged}
></ha-checkbox>

View File

@ -9,7 +9,7 @@ import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-svg-icon";
import type { BackupConfig } from "../../../../../data/backup";
import type { BackupAgent, BackupConfig } from "../../../../../data/backup";
import {
BackupScheduleRecurrence,
computeBackupAgentName,
@ -25,6 +25,8 @@ class HaBackupBackupsSummary extends LitElement {
@property({ attribute: false }) public config!: BackupConfig;
@property({ attribute: false }) public agents!: BackupAgent[];
private _configure() {
navigate("/config/backup/settings");
}
@ -160,7 +162,7 @@ class HaBackupBackupsSummary extends LitElement {
const name = computeBackupAgentName(
this.hass.localize,
offsiteLocations[0],
offsiteLocations
this.agents
);
return this.hass.localize(
"ui.panel.config.backup.overview.settings.locations_one",

View File

@ -18,6 +18,7 @@ import "../../../../components/ha-md-select";
import "../../../../components/ha-md-select-option";
import "../../../../components/ha-textfield";
import type {
BackupAgent,
BackupConfig,
GenerateBackupParams,
} from "../../../../data/backup";
@ -64,7 +65,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
@state() private _step?: "data" | "sync";
@state() private _agentIds: string[] = [];
@state() private _agents: BackupAgent[] = [];
@state() private _backupConfig?: BackupConfig;
@ -89,7 +90,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
}
this._step = undefined;
this._formData = undefined;
this._agentIds = [];
this._agents = [];
this._backupConfig = undefined;
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
@ -97,15 +98,14 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
private async _fetchAgents() {
const { agents } = await fetchBackupAgentsInfo(this.hass);
this._agentIds = agents
.map((agent) => agent.agent_id)
this._agents = agents
.filter(
(id) =>
id !== CLOUD_AGENT ||
(agent) =>
agent.agent_id !== CLOUD_AGENT ||
(this._params?.cloudStatus?.logged_in &&
this._params?.cloudStatus?.active_subscription)
)
.sort(compareAgents);
.sort((a, b) => compareAgents(a.agent_id, b.agent_id));
}
private async _fetchBackupConfig() {
@ -134,6 +134,10 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
this._step = STEPS[index + 1];
}
private get _allAgentIds() {
return this._agents.map((agent) => agent.agent_id);
}
protected willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties);
@ -144,7 +148,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
// Remove disallowed agents from the list
const agentsIds =
this._formData.agents_mode === "all"
? this._agentIds
? this._allAgentIds
: this._formData.agent_ids;
const filteredAgents = agentsIds.filter(
@ -309,7 +313,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
<div slot="headline">
${this.hass.localize(
"ui.panel.config.backup.dialogs.generate.sync.locations_options.all",
{ count: this._agentIds.length }
{ count: this._allAgentIds.length }
)}
</div>
</ha-md-select-option>
@ -350,7 +354,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
.hass=${this.hass}
.value=${this._formData.agent_ids}
@value-changed=${this._agentsChanged}
.agentIds=${this._agentIds}
.agents=${this._agents}
.disabledAgentIds=${disabledAgentIds}
></ha-backup-agents-picker>
</ha-expansion-panel>
@ -385,7 +389,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
if (!this._formData) {
return [];
}
const allAgents = this._agentIds;
const allAgents = this._allAgentIds;
return !this._formData.data.include_homeassistant
? DISALLOWED_AGENTS_NO_HA.filter((agentId) => allAgents.includes(agentId))
: [];
@ -403,7 +407,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
const params: GenerateBackupParams = {
name,
password,
agent_ids: agents_mode === "all" ? this._agentIds : agent_ids,
agent_ids: agents_mode === "all" ? this._allAgentIds : agent_ids,
// We always include homeassistant if we include database
include_homeassistant:
data.include_homeassistant || data.include_database,

View File

@ -33,7 +33,11 @@ import "../../../components/ha-icon-next";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-list-item";
import "../../../components/ha-svg-icon";
import type { BackupConfig, BackupContent } from "../../../data/backup";
import type {
BackupAgent,
BackupConfig,
BackupContent,
} from "../../../data/backup";
import {
computeBackupAgentName,
deleteBackup,
@ -86,6 +90,8 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public config?: BackupConfig;
@property({ attribute: false }) public agents: BackupAgent[] = [];
@state() private _selected: string[] = [];
@storage({
@ -169,7 +175,7 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
const name = computeBackupAgentName(
this.hass.localize,
agentId,
backup.agent_ids
this.agents
);
if (isLocalAgent(agentId)) {
return html`

View File

@ -21,6 +21,7 @@ import "../../../components/ha-list-item";
import "../../../components/ha-md-list";
import "../../../components/ha-md-list-item";
import type {
BackupAgent,
BackupConfig,
BackupContentExtended,
BackupData,
@ -70,6 +71,8 @@ class HaConfigBackupDetails extends LitElement {
@property({ attribute: false }) public config?: BackupConfig;
@property({ attribute: false }) public agents: BackupAgent[] = [];
@state() private _backup?: BackupContentExtended | null;
@state() private _agents: Agent[] = [];
@ -232,7 +235,7 @@ class HaConfigBackupDetails extends LitElement {
const name = computeBackupAgentName(
this.hass.localize,
agentId,
this._backup!.agent_ids
this.agents
);
return html`

View File

@ -13,11 +13,14 @@ import "../../../components/ha-icon-next";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-list-item";
import "../../../components/ha-svg-icon";
import type {
BackupAgent,
BackupConfig,
BackupContent,
} from "../../../data/backup";
import {
generateBackup,
generateBackupWithAutomaticSettings,
type BackupConfig,
type BackupContent,
} from "../../../data/backup";
import type { ManagerStateEvent } from "../../../data/backup_manager";
import type { CloudStatus } from "../../../data/cloud";
@ -53,6 +56,8 @@ class HaConfigBackupOverview extends LitElement {
@property({ attribute: false }) public config?: BackupConfig;
@property({ attribute: false }) public agents: BackupAgent[] = [];
private async _uploadBackup(ev) {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
@ -184,6 +189,7 @@ class HaConfigBackupOverview extends LitElement {
<ha-backup-overview-settings
.hass=${this.hass}
.config=${this.config!}
.agents=${this.agents}
></ha-backup-overview-settings>
`
: nothing}

View File

@ -16,7 +16,7 @@ import "../../../components/ha-list-item";
import "../../../components/ha-alert";
import "../../../components/ha-password-field";
import "../../../components/ha-svg-icon";
import type { BackupConfig } from "../../../data/backup";
import type { BackupAgent, BackupConfig } from "../../../data/backup";
import { updateBackupConfig } from "../../../data/backup";
import type { CloudStatus } from "../../../data/cloud";
import "../../../layouts/hass-subpage";
@ -39,6 +39,8 @@ class HaConfigBackupSettings extends LitElement {
@property({ attribute: false }) public config?: BackupConfig;
@property({ attribute: false }) public agents: BackupAgent[] = [];
@state() private _config?: BackupConfig;
protected willUpdate(changedProperties: PropertyValues): void {
@ -178,6 +180,7 @@ class HaConfigBackupSettings extends LitElement {
.hass=${this.hass}
.value=${this._config.create_backup.agent_ids}
.cloudStatus=${this.cloudStatus}
.agents=${this.agents}
@value-changed=${this._agentsConfigChanged}
></ha-backup-config-agents>
${!this._config.create_backup.agent_ids.length

View File

@ -1,9 +1,14 @@
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { BackupConfig, BackupContent } from "../../../data/backup";
import type {
BackupAgent,
BackupConfig,
BackupContent,
} from "../../../data/backup";
import {
compareAgents,
fetchBackupAgentsInfo,
fetchBackupConfig,
fetchBackupInfo,
} from "../../../data/backup";
@ -41,6 +46,8 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) {
@state() private _backups: BackupContent[] = [];
@state() private _agents: BackupAgent[] = [];
@state() private _fetching = false;
@state() private _config?: BackupConfig;
@ -54,15 +61,20 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) {
this.addEventListener("ha-refresh-backup-config", () => {
this._fetchBackupConfig();
});
this.addEventListener("ha-refresh-backup-agents", () => {
this._fetchBackupAgents();
});
}
private _fetchAll() {
this._fetching = true;
Promise.all([this._fetchBackupInfo(), this._fetchBackupConfig()]).finally(
() => {
this._fetching = false;
}
);
Promise.all([
this._fetchBackupInfo(),
this._fetchBackupConfig(),
this._fetchBackupAgents(),
]).finally(() => {
this._fetching = false;
});
}
public connectedCallback() {
@ -70,6 +82,7 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) {
if (this.hasUpdated) {
this._fetchBackupInfo();
this._fetchBackupConfig();
this._fetchBackupAgents();
}
}
@ -87,6 +100,11 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) {
this._config = config;
}
private async _fetchBackupAgents() {
const { agents } = await fetchBackupAgentsInfo(this.hass);
this._agents = agents.sort((a, b) => compareAgents(a.agent_id, b.agent_id));
}
protected routerOptions: RouterOptions = {
defaultPage: "overview",
routes: {
@ -117,6 +135,7 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) {
pageEl.manager = this._manager;
pageEl.backups = this._backups;
pageEl.config = this._config;
pageEl.agents = this._agents;
pageEl.fetching = this._fetching;
if (