Skip to content

Commit

Permalink
Don't allow user to choose cloud if ha settings is not set (#23364)
Browse files Browse the repository at this point in the history
  • Loading branch information
piitaya authored Dec 20, 2024
1 parent 1f7929b commit dc799bf
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 130 deletions.
31 changes: 14 additions & 17 deletions src/panels/config/backup/components/ha-backup-agents-picker.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import { mdiHarddisk } from "@mdi/js";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeDomain } from "../../../../common/entity/compute_domain";
import "../../../../components/ha-checkbox";
import "../../../../components/ha-formfield";
import "../../../../components/ha-svg-icon";
import {
compareAgents,
computeBackupAgentName,
isLocalAgent,
type BackupAgent,
} from "../../../../data/backup";
import { computeBackupAgentName, isLocalAgent } from "../../../../data/backup";
import type { HomeAssistant } from "../../../../types";
import { brandsUrl } from "../../../../util/brands-url";

Expand All @@ -25,22 +20,18 @@ class HaBackupAgentsPicker extends LitElement {
public disabled = false;

@property({ attribute: false })
public agents!: BackupAgent[];
public agentIds!: string[];

@property({ attribute: false })
public disabledAgents?: string[];
public disabledAgentIds?: string[];

@property({ attribute: false })
public value!: string[];

private _agentIds = memoizeOne((agents: BackupAgent[]) =>
agents.map((agent) => agent.agent_id).sort(compareAgents)
);

render() {
return html`
<div class="agents">
${this._agentIds(this.agents).map((agent) => this._renderAgent(agent))}
${this.agentIds.map((agent) => this._renderAgent(agent))}
</div>
`;
}
Expand All @@ -50,15 +41,15 @@ class HaBackupAgentsPicker extends LitElement {
const name = computeBackupAgentName(
this.hass.localize,
agentId,
this._agentIds(this.agents)
this.agentIds
);

const disabled =
this.disabled || this.disabledAgents?.includes(agentId) || false;
this.disabled || this.disabledAgentIds?.includes(agentId) || false;

return html`
<ha-formfield>
<span class="label" slot="label">
<span class="label ${classMap({ disabled })}" slot="label">
${isLocalAgent(agentId)
? html`
<ha-svg-icon .path=${mdiHarddisk} slot="start"> </ha-svg-icon>
Expand Down Expand Up @@ -127,6 +118,12 @@ class HaBackupAgentsPicker extends LitElement {
line-height: 24px;
letter-spacing: 0.5px;
}
span.disabled {
color: var(--disabled-text-color);
}
span.disabled ha-svg-icon {
color: var(--disabled-text-color);
}
`;
}

Expand Down
105 changes: 92 additions & 13 deletions src/panels/config/backup/dialogs/dialog-generate-backup.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { mdiClose } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-expansion-panel";
Expand All @@ -17,20 +18,21 @@ import "../../../../components/ha-md-select";
import "../../../../components/ha-md-select-option";
import "../../../../components/ha-textfield";
import type {
BackupAgent,
BackupConfig,
GenerateBackupParams,
} from "../../../../data/backup";
import {
CLOUD_AGENT,
compareAgents,
fetchBackupAgentsInfo,
fetchBackupConfig,
} from "../../../../data/backup";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import "../components/ha-backup-agents-picker";
import "../components/config/ha-backup-config-data";
import type { BackupConfigData } from "../components/config/ha-backup-config-data";
import "../components/ha-backup-agents-picker";
import type { GenerateBackupDialogParams } from "./show-dialog-generate-backup";

type FormData = {
Expand All @@ -54,13 +56,15 @@ const INITIAL_DATA: FormData = {

const STEPS = ["data", "sync"] as const;

const DISALLOWED_AGENTS_NO_HA = [CLOUD_AGENT];

@customElement("ha-dialog-generate-backup")
class DialogGenerateBackup extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;

@state() private _step?: "data" | "sync";

@state() private _agents: BackupAgent[] = [];
@state() private _agentIds: string[] = [];

@state() private _backupConfig?: BackupConfig;

Expand All @@ -74,6 +78,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
this._step = STEPS[0];
this._formData = INITIAL_DATA;
this._params = _params;

this._fetchAgents();
this._fetchBackupConfig();
}
Expand All @@ -84,15 +89,18 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
}
this._step = undefined;
this._formData = undefined;
this._agents = [];
this._agentIds = [];
this._backupConfig = undefined;
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}

private async _fetchAgents() {
const { agents } = await fetchBackupAgentsInfo(this.hass);
this._agents = agents;
this._agentIds = agents
.map((agent) => agent.agent_id)
.filter((id) => id !== CLOUD_AGENT || this._params?.cloudStatus.logged_in)
.sort(compareAgents);
}

private async _fetchBackupConfig() {
Expand Down Expand Up @@ -120,6 +128,32 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
this._step = STEPS[index + 1];
}

protected willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties);

if (changedProperties.has("_step")) {
if (this._step === "sync" && this._formData) {
const disallowedAgents = this._disabledAgentIds();
if (disallowedAgents.length) {
// Remove disallowed agents from the list
const agentsIds =
this._formData.agents_mode === "all"
? this._agentIds
: this._formData.agent_ids;

const filteredAgents = agentsIds.filter(
(agentId) => !disallowedAgents.includes(agentId)
);
this._formData = {
...this._formData,
agents_mode: "custom",
agent_ids: filteredAgents,
};
}
}
}
}

protected render() {
if (!this._step || !this._formData) {
return nothing;
Expand All @@ -131,6 +165,8 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
const isFirstStep = this._step === STEPS[0];
const isLastStep = this._step === STEPS[STEPS.length - 1];

const selectedAgents = this._formData.agent_ids;

return html`
<ha-md-dialog open disable-cancel-action @closed=${this._dialogClosed}>
<ha-dialog-header slot="headline">
Expand Down Expand Up @@ -159,7 +195,14 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
? html`<ha-button @click=${this.closeDialog}>Cancel</ha-button>`
: nothing}
${isLastStep
? html`<ha-button @click=${this._submit}>Create backup</ha-button>`
? html`
<ha-button
@click=${this._submit}
.disabled=${!selectedAgents.length}
>
Create backup
</ha-button>
`
: html`<ha-button @click=${this._nextStep}>Next</ha-button>`}
</div>
</ha-md-dialog>
Expand Down Expand Up @@ -194,6 +237,8 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
return nothing;
}

const disabledAgentIds = this._disabledAgentIds();

return html`
<ha-textfield
name="name"
Expand All @@ -214,23 +259,38 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
@change=${this._selectChanged}
.value=${this._formData.agents_mode}
>
<ha-md-select-option value="all">
<div slot="headline">All (${this._agents.length})</div>
<ha-md-select-option
value="all"
.disabled=${disabledAgentIds.length}
>
<div slot="headline">All (${this._agentIds.length})</div>
</ha-md-select-option>
<ha-md-select-option value="custom">
<div slot="headline">Custom</div>
</ha-md-select-option>
</ha-md-select>
</ha-md-list-item>
</ha-md-list>
${disabledAgentIds.length
? html`
<ha-alert
alert-type="info"
.title=${"Home Assistant Cloud cannot synchronize"}
>
Add Home Assistant settings data to synchronize this backup to
Home Assistant Cloud.
</ha-alert>
`
: nothing}
${this._formData.agents_mode === "custom"
? html`
<ha-expansion-panel .header=${"Locations"} outlined expanded>
<ha-backup-agents-picker
.hass=${this.hass}
.value=${this._formData.agent_ids}
@value-changed=${this._agentsChanged}
.agents=${this._agents}
.agentIds=${this._agentIds}
.disabledAgentIds=${disabledAgentIds}
></ha-backup-agents-picker>
</ha-expansion-panel>
`
Expand Down Expand Up @@ -260,6 +320,16 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
};
}

private _disabledAgentIds() {
if (!this._formData) {
return [];
}
const allAgents = this._agentIds;
return !this._formData.data.include_homeassistant
? DISALLOWED_AGENTS_NO_HA.filter((agentId) => allAgents.includes(agentId))
: [];
}

private async _submit() {
if (!this._formData) {
return;
Expand All @@ -269,12 +339,10 @@ class DialogGenerateBackup extends LitElement implements HassDialog {

const password = this._backupConfig?.create_backup.password || undefined;

const ALL_AGENT_IDS = this._agents.map((agent) => agent.agent_id);

const params: GenerateBackupParams = {
name,
password,
agent_ids: agents_mode === "all" ? ALL_AGENT_IDS : agent_ids,
agent_ids: agents_mode === "all" ? this._agentIds : agent_ids,
// We always include homeassistant if we include database
include_homeassistant:
data.include_homeassistant || data.include_database,
Expand All @@ -287,6 +355,13 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
params.include_addons = data.include_addons;
}

// Ensure we don't upload to disallowed agents if we are not including homeassistant
if (!params.include_homeassistant) {
params.agent_ids = params.agent_ids.filter(
(agentId) => !DISALLOWED_AGENTS_NO_HA.includes(agentId)
);
}

this._params!.submit?.(params);
this.closeDialog();
}
Expand Down Expand Up @@ -333,6 +408,10 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
.content {
padding-top: 0;
}
ha-alert {
margin-bottom: 16px;
display: block;
}
`,
];
}
Expand Down
Loading

0 comments on commit dc799bf

Please sign in to comment.