Add guards for network storage (#16591)

This commit is contained in:
Joakim Sørensen 2023-05-23 13:33:12 +03:00 committed by GitHub
parent 4ccfd6a3fc
commit 22dc757382
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 30 deletions

View File

@ -17,6 +17,10 @@ export enum SupervisorMountState {
UNKNOWN = "unknown",
}
interface MountOptions {
default_backup_mount?: string | null;
}
interface SupervisorMountBase {
name: string;
usage: SupervisorMountUsage;
@ -53,6 +57,7 @@ export type SupervisorMountRequestParams =
| SupervisorCIFSMountRequestParams;
export interface SupervisorMounts {
default_backup_mount: string | null;
mounts: SupervisorMount[];
}
@ -111,3 +116,15 @@ export const reloadSupervisorMount = async (
method: "post",
timeout: null,
});
export const changeMountOptions = async (
hass: HomeAssistant,
data: MountOptions
): Promise<void> =>
hass.callWS({
type: "supervisor/api",
endpoint: `/mounts/options`,
method: "post",
timeout: null,
data,
});

View File

@ -15,6 +15,7 @@ import "../../../components/ha-button-menu";
import "../../../components/ha-icon-button";
import "../../../components/ha-metric";
import "../../../components/ha-svg-icon";
import "../../../components/ha-icon-next";
import { extractApiErrorMessage } from "../../../data/hassio/common";
import { HassioHostInfo, fetchHassioHostInfo } from "../../../data/hassio/host";
import {
@ -22,6 +23,7 @@ import {
SupervisorMountState,
SupervisorMountType,
SupervisorMountUsage,
SupervisorMounts,
fetchSupervisorMounts,
reloadSupervisorMount,
} from "../../../data/supervisor/mounts";
@ -35,6 +37,7 @@ import {
import "../core/ha-config-analytics";
import { showMoveDatadiskDialog } from "./show-dialog-move-datadisk";
import { showMountViewDialog } from "./show-dialog-view-mount";
import { navigate } from "../../../common/navigate";
@customElement("ha-config-section-storage")
class HaConfigSectionStorage extends LitElement {
@ -48,7 +51,7 @@ class HaConfigSectionStorage extends LitElement {
@state() private _hostInfo?: HassioHostInfo;
@state() private _mounts?: SupervisorMount[];
@state() private _mountsInfo?: SupervisorMounts | null;
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
@ -57,7 +60,14 @@ class HaConfigSectionStorage extends LitElement {
}
}
protected render(): TemplateResult {
protected render(): TemplateResult | typeof nothing {
if (this._mountsInfo === undefined) {
return nothing;
}
const validMounts = this._mountsInfo?.mounts.filter((mount) =>
[SupervisorMountType.CIFS, SupervisorMountType.NFS].includes(mount.type)
);
const isHAOS = this._hostInfo?.features.includes("haos");
return html`
<hass-subpage
back-path="/config/system"
@ -121,21 +131,46 @@ class HaConfigSectionStorage extends LitElement {
</ha-card>
`
: ""}
<ha-card
outlined
.header=${this.hass.localize(
"ui.panel.config.storage.network_mounts.title"
)}
>
${this._mounts?.length
${this._mountsInfo === null
? html`<ha-alert
class="mounts-not-supported"
alert-type="warning"
.title=${this.hass.localize(
"ui.panel.config.storage.network_mounts.not_supported.title"
)}
>
${isHAOS
? html`${this.hass.localize(
"ui.panel.config.storage.network_mounts.not_supported.haos"
)}
<mwc-button
slot="action"
@click=${this._navigateToUpdates}
>
${this.hass.localize(
"ui.panel.config.storage.network_mounts.not_supported.navigate_to_updates"
)}
</mwc-button>`
: this.hass.localize(
"ui.panel.config.storage.network_mounts.not_supported.supervised"
)}
</ha-alert>`
: validMounts?.length
? html`<mwc-list>
${this._mounts.map(
${validMounts.map(
(mount) => html`
<ha-list-item
graphic="avatar"
.mount=${mount}
twoline
.hasMeta=${mount.state !== SupervisorMountState.ACTIVE}
hasMeta
@click=${this._changeMount}
>
<div slot="graphic">
@ -155,18 +190,20 @@ class HaConfigSectionStorage extends LitElement {
? mount.path
: ` :${mount.share}`}
</span>
<ha-icon-button
class="reload-btn"
slot="meta"
.mount=${mount}
@click=${this._reloadMount}
.path=${mdiReload}
></ha-icon-button>
${mount.state !== SupervisorMountState.ACTIVE
? html`<ha-icon-button
class="reload-btn"
slot="meta"
.mount=${mount}
@click=${this._reloadMount}
.path=${mdiReload}
></ha-icon-button>`
: html`<ha-icon-next slot="meta"></ha-icon-next>`}
</ha-list-item>
`
)}
</mwc-list>`
: html` <div class="no-mounts">
: html`<div class="no-mounts">
<ha-svg-icon .path=${mdiNas}></ha-svg-icon>
<p>
${this.hass.localize(
@ -174,14 +211,15 @@ class HaConfigSectionStorage extends LitElement {
)}
</p>
</div>`}
<div class="card-actions">
<mwc-button @click=${this._addMount}>
${this.hass.localize(
"ui.panel.config.storage.network_mounts.add_title"
)}
</mwc-button>
</div>
${this._mountsInfo !== null
? html`<div class="card-actions">
<mwc-button @click=${this._addMount}>
${this.hass.localize(
"ui.panel.config.storage.network_mounts.add_title"
)}
</mwc-button>
</div>`
: nothing}
</ha-card>
</div>
</hass-subpage>
@ -190,13 +228,15 @@ class HaConfigSectionStorage extends LitElement {
private async _load() {
try {
[this._hostInfo] = await Promise.all([
fetchHassioHostInfo(this.hass),
this._reloadMounts(),
]);
this._hostInfo = await fetchHassioHostInfo(this.hass);
} catch (err: any) {
this._error = err.message || err;
}
if (this._hostInfo?.features.includes("mount")) {
await this._reloadMounts();
} else {
this._mountsInfo = null;
}
}
private _moveDatadisk(): void {
@ -205,6 +245,10 @@ class HaConfigSectionStorage extends LitElement {
});
}
private async _navigateToUpdates(): Promise<void> {
navigate("/config/updates");
}
private async _reloadMount(ev: Event): Promise<void> {
ev.stopPropagation();
const mount: SupervisorMount = (ev.currentTarget as any).mount;
@ -224,7 +268,9 @@ class HaConfigSectionStorage extends LitElement {
}
private _addMount(): void {
showMountViewDialog(this, { reloadMounts: this._reloadMounts });
showMountViewDialog(this, {
reloadMounts: this._reloadMounts,
});
}
private _changeMount(ev: Event): void {
@ -237,12 +283,10 @@ class HaConfigSectionStorage extends LitElement {
private async _reloadMounts(): Promise<void> {
try {
const allMounts = await fetchSupervisorMounts(this.hass);
this._mounts = allMounts.mounts.filter((mount) =>
[SupervisorMountType.CIFS, SupervisorMountType.NFS].includes(mount.type)
);
this._mountsInfo = await fetchSupervisorMounts(this.hass);
} catch (err: any) {
this._error = err.message || err;
this._mountsInfo = null;
}
}
@ -274,6 +318,10 @@ class HaConfigSectionStorage extends LitElement {
color: var(--warning-color);
}
.mounts-not-supported {
padding: 0 16px 16px;
}
.reload-btn {
float: right;
position: relative;
@ -295,6 +343,14 @@ class HaConfigSectionStorage extends LitElement {
border-radius: 50%;
margin-bottom: 8px;
}
ha-list-item {
--mdc-list-item-meta-size: auto;
--mdc-list-item-meta-display: flex;
}
ha-svg-icon,
ha-icon-next {
width: 24px;
}
`;
}

View File

@ -3987,6 +3987,12 @@
"add_title": "Add network storage",
"update_title": "Update network storage",
"no_mounts": "No connected network storage",
"not_supported": {
"title": "The operating system does not support network storage",
"supervised": "Network storage is not supported on this host",
"haos": "To use network storage you need to run at least Home Assistant Operating System 10.0",
"navigate_to_updates": "Go to updates"
},
"mount_usage": {
"backup": "Backup",
"media": "Media"
@ -4008,6 +4014,10 @@
"title": "Server",
"description": "This is the domain name (FQDN) or IP address of the storage server you want to connect to"
},
"default_backup_mount": {
"title": "Default backup location",
"description": "This will be the default location for backups"
},
"path": {
"title": "Remote share path",
"description": "This is the path of the remote share on your storage server"