mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 10:16:46 +00:00
Add guards for network storage (#16591)
This commit is contained in:
parent
4ccfd6a3fc
commit
22dc757382
@ -17,6 +17,10 @@ export enum SupervisorMountState {
|
|||||||
UNKNOWN = "unknown",
|
UNKNOWN = "unknown",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MountOptions {
|
||||||
|
default_backup_mount?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
interface SupervisorMountBase {
|
interface SupervisorMountBase {
|
||||||
name: string;
|
name: string;
|
||||||
usage: SupervisorMountUsage;
|
usage: SupervisorMountUsage;
|
||||||
@ -53,6 +57,7 @@ export type SupervisorMountRequestParams =
|
|||||||
| SupervisorCIFSMountRequestParams;
|
| SupervisorCIFSMountRequestParams;
|
||||||
|
|
||||||
export interface SupervisorMounts {
|
export interface SupervisorMounts {
|
||||||
|
default_backup_mount: string | null;
|
||||||
mounts: SupervisorMount[];
|
mounts: SupervisorMount[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,3 +116,15 @@ export const reloadSupervisorMount = async (
|
|||||||
method: "post",
|
method: "post",
|
||||||
timeout: null,
|
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,
|
||||||
|
});
|
||||||
|
@ -15,6 +15,7 @@ import "../../../components/ha-button-menu";
|
|||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-metric";
|
import "../../../components/ha-metric";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
|
import "../../../components/ha-icon-next";
|
||||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||||
import { HassioHostInfo, fetchHassioHostInfo } from "../../../data/hassio/host";
|
import { HassioHostInfo, fetchHassioHostInfo } from "../../../data/hassio/host";
|
||||||
import {
|
import {
|
||||||
@ -22,6 +23,7 @@ import {
|
|||||||
SupervisorMountState,
|
SupervisorMountState,
|
||||||
SupervisorMountType,
|
SupervisorMountType,
|
||||||
SupervisorMountUsage,
|
SupervisorMountUsage,
|
||||||
|
SupervisorMounts,
|
||||||
fetchSupervisorMounts,
|
fetchSupervisorMounts,
|
||||||
reloadSupervisorMount,
|
reloadSupervisorMount,
|
||||||
} from "../../../data/supervisor/mounts";
|
} from "../../../data/supervisor/mounts";
|
||||||
@ -35,6 +37,7 @@ import {
|
|||||||
import "../core/ha-config-analytics";
|
import "../core/ha-config-analytics";
|
||||||
import { showMoveDatadiskDialog } from "./show-dialog-move-datadisk";
|
import { showMoveDatadiskDialog } from "./show-dialog-move-datadisk";
|
||||||
import { showMountViewDialog } from "./show-dialog-view-mount";
|
import { showMountViewDialog } from "./show-dialog-view-mount";
|
||||||
|
import { navigate } from "../../../common/navigate";
|
||||||
|
|
||||||
@customElement("ha-config-section-storage")
|
@customElement("ha-config-section-storage")
|
||||||
class HaConfigSectionStorage extends LitElement {
|
class HaConfigSectionStorage extends LitElement {
|
||||||
@ -48,7 +51,7 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
|
|
||||||
@state() private _hostInfo?: HassioHostInfo;
|
@state() private _hostInfo?: HassioHostInfo;
|
||||||
|
|
||||||
@state() private _mounts?: SupervisorMount[];
|
@state() private _mountsInfo?: SupervisorMounts | null;
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
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`
|
return html`
|
||||||
<hass-subpage
|
<hass-subpage
|
||||||
back-path="/config/system"
|
back-path="/config/system"
|
||||||
@ -121,21 +131,46 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
outlined
|
||||||
.header=${this.hass.localize(
|
.header=${this.hass.localize(
|
||||||
"ui.panel.config.storage.network_mounts.title"
|
"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>
|
? html`<mwc-list>
|
||||||
${this._mounts.map(
|
${validMounts.map(
|
||||||
(mount) => html`
|
(mount) => html`
|
||||||
<ha-list-item
|
<ha-list-item
|
||||||
graphic="avatar"
|
graphic="avatar"
|
||||||
.mount=${mount}
|
.mount=${mount}
|
||||||
twoline
|
twoline
|
||||||
.hasMeta=${mount.state !== SupervisorMountState.ACTIVE}
|
hasMeta
|
||||||
@click=${this._changeMount}
|
@click=${this._changeMount}
|
||||||
>
|
>
|
||||||
<div slot="graphic">
|
<div slot="graphic">
|
||||||
@ -155,18 +190,20 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
? mount.path
|
? mount.path
|
||||||
: ` :${mount.share}`}
|
: ` :${mount.share}`}
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-button
|
${mount.state !== SupervisorMountState.ACTIVE
|
||||||
class="reload-btn"
|
? html`<ha-icon-button
|
||||||
slot="meta"
|
class="reload-btn"
|
||||||
.mount=${mount}
|
slot="meta"
|
||||||
@click=${this._reloadMount}
|
.mount=${mount}
|
||||||
.path=${mdiReload}
|
@click=${this._reloadMount}
|
||||||
></ha-icon-button>
|
.path=${mdiReload}
|
||||||
|
></ha-icon-button>`
|
||||||
|
: html`<ha-icon-next slot="meta"></ha-icon-next>`}
|
||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</mwc-list>`
|
</mwc-list>`
|
||||||
: html` <div class="no-mounts">
|
: html`<div class="no-mounts">
|
||||||
<ha-svg-icon .path=${mdiNas}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiNas}></ha-svg-icon>
|
||||||
<p>
|
<p>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -174,14 +211,15 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>`}
|
</div>`}
|
||||||
|
${this._mountsInfo !== null
|
||||||
<div class="card-actions">
|
? html`<div class="card-actions">
|
||||||
<mwc-button @click=${this._addMount}>
|
<mwc-button @click=${this._addMount}>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.storage.network_mounts.add_title"
|
"ui.panel.config.storage.network_mounts.add_title"
|
||||||
)}
|
)}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</div>`
|
||||||
|
: nothing}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
</div>
|
</div>
|
||||||
</hass-subpage>
|
</hass-subpage>
|
||||||
@ -190,13 +228,15 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
|
|
||||||
private async _load() {
|
private async _load() {
|
||||||
try {
|
try {
|
||||||
[this._hostInfo] = await Promise.all([
|
this._hostInfo = await fetchHassioHostInfo(this.hass);
|
||||||
fetchHassioHostInfo(this.hass),
|
|
||||||
this._reloadMounts(),
|
|
||||||
]);
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err.message || err;
|
this._error = err.message || err;
|
||||||
}
|
}
|
||||||
|
if (this._hostInfo?.features.includes("mount")) {
|
||||||
|
await this._reloadMounts();
|
||||||
|
} else {
|
||||||
|
this._mountsInfo = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _moveDatadisk(): void {
|
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> {
|
private async _reloadMount(ev: Event): Promise<void> {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const mount: SupervisorMount = (ev.currentTarget as any).mount;
|
const mount: SupervisorMount = (ev.currentTarget as any).mount;
|
||||||
@ -224,7 +268,9 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _addMount(): void {
|
private _addMount(): void {
|
||||||
showMountViewDialog(this, { reloadMounts: this._reloadMounts });
|
showMountViewDialog(this, {
|
||||||
|
reloadMounts: this._reloadMounts,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _changeMount(ev: Event): void {
|
private _changeMount(ev: Event): void {
|
||||||
@ -237,12 +283,10 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
|
|
||||||
private async _reloadMounts(): Promise<void> {
|
private async _reloadMounts(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const allMounts = await fetchSupervisorMounts(this.hass);
|
this._mountsInfo = await fetchSupervisorMounts(this.hass);
|
||||||
this._mounts = allMounts.mounts.filter((mount) =>
|
|
||||||
[SupervisorMountType.CIFS, SupervisorMountType.NFS].includes(mount.type)
|
|
||||||
);
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = err.message || err;
|
this._error = err.message || err;
|
||||||
|
this._mountsInfo = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +318,10 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
color: var(--warning-color);
|
color: var(--warning-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mounts-not-supported {
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.reload-btn {
|
.reload-btn {
|
||||||
float: right;
|
float: right;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -295,6 +343,14 @@ class HaConfigSectionStorage extends LitElement {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-bottom: 8px;
|
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;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3987,6 +3987,12 @@
|
|||||||
"add_title": "Add network storage",
|
"add_title": "Add network storage",
|
||||||
"update_title": "Update network storage",
|
"update_title": "Update network storage",
|
||||||
"no_mounts": "No connected 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": {
|
"mount_usage": {
|
||||||
"backup": "Backup",
|
"backup": "Backup",
|
||||||
"media": "Media"
|
"media": "Media"
|
||||||
@ -4008,6 +4014,10 @@
|
|||||||
"title": "Server",
|
"title": "Server",
|
||||||
"description": "This is the domain name (FQDN) or IP address of the storage server you want to connect to"
|
"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": {
|
"path": {
|
||||||
"title": "Remote share path",
|
"title": "Remote share path",
|
||||||
"description": "This is the path of the remote share on your storage server"
|
"description": "This is the path of the remote share on your storage server"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user