mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-18 06:46:35 +00:00
Add dialog to trigger moving datadisk (#10048)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
ea51186767
commit
a3a08ff5c7
191
hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
Normal file
191
hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import "../../../../src/components/ha-circular-progress";
|
||||||
|
import "../../../../src/components/ha-markdown";
|
||||||
|
import {
|
||||||
|
extractApiErrorMessage,
|
||||||
|
ignoreSupervisorError,
|
||||||
|
} from "../../../../src/data/hassio/common";
|
||||||
|
import {
|
||||||
|
DatadiskList,
|
||||||
|
listDatadisks,
|
||||||
|
moveDatadisk,
|
||||||
|
} from "../../../../src/data/hassio/host";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
|
||||||
|
|
||||||
|
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
|
||||||
|
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
|
||||||
|
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
|
||||||
|
const rebootTime = (supervisor.host.startup_time * 4) / 60;
|
||||||
|
return Math.ceil((moveTime + rebootTime) / 10) * 10;
|
||||||
|
});
|
||||||
|
|
||||||
|
@customElement("dialog-hassio-datadisk")
|
||||||
|
class HassioDatadiskDialog extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private dialogParams?: HassioDatatiskDialogParams;
|
||||||
|
|
||||||
|
@state() private selectedDevice?: string;
|
||||||
|
|
||||||
|
@state() private devices?: DatadiskList["devices"];
|
||||||
|
|
||||||
|
@state() private moving = false;
|
||||||
|
|
||||||
|
public showDialog(params: HassioDatatiskDialogParams) {
|
||||||
|
this.dialogParams = params;
|
||||||
|
listDatadisks(this.hass).then((data) => {
|
||||||
|
this.devices = data.devices;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this.dialogParams = undefined;
|
||||||
|
this.selectedDevice = undefined;
|
||||||
|
this.devices = undefined;
|
||||||
|
this.moving = false;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.dialogParams) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
scrimClickAction
|
||||||
|
escapeKeyAction
|
||||||
|
@closed=${this.closeDialog}
|
||||||
|
?hideActions=${this.moving}
|
||||||
|
>
|
||||||
|
${this.moving
|
||||||
|
? html`<slot name="heading">
|
||||||
|
<h2 id="title" class="header_title">
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.moving"
|
||||||
|
)}
|
||||||
|
</h2>
|
||||||
|
</slot>
|
||||||
|
<ha-circular-progress alt="Moving" size="large" active>
|
||||||
|
</ha-circular-progress>
|
||||||
|
<p class="progress-text">
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.moving_desc"
|
||||||
|
)}
|
||||||
|
</p>`
|
||||||
|
: html`<slot name="heading">
|
||||||
|
<h2 id="title" class="header_title">
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.title"
|
||||||
|
)}
|
||||||
|
</h2>
|
||||||
|
</slot>
|
||||||
|
${this.devices?.length
|
||||||
|
? html`
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.description",
|
||||||
|
{
|
||||||
|
current_path: this.dialogParams.supervisor.os.data_disk,
|
||||||
|
time: calculateMoveTime(this.dialogParams.supervisor),
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<paper-dropdown-menu
|
||||||
|
.label=${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.select_device"
|
||||||
|
)}
|
||||||
|
@value-changed=${this._select_device}
|
||||||
|
>
|
||||||
|
<paper-listbox slot="dropdown-content">
|
||||||
|
${this.devices.map(
|
||||||
|
(device) => html`<paper-item>${device}</paper-item>`
|
||||||
|
)}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
`
|
||||||
|
: this.devices === undefined
|
||||||
|
? this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.loading_devices"
|
||||||
|
)
|
||||||
|
: this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.no_devices"
|
||||||
|
)}
|
||||||
|
|
||||||
|
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.cancel"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
|
||||||
|
<mwc-button
|
||||||
|
.disabled=${!this.selectedDevice}
|
||||||
|
slot="primaryAction"
|
||||||
|
@click=${this._moveDatadisk}
|
||||||
|
>
|
||||||
|
${this.dialogParams.supervisor.localize(
|
||||||
|
"dialog.datadisk_move.move"
|
||||||
|
)}
|
||||||
|
</mwc-button>`}
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _select_device(event) {
|
||||||
|
this.selectedDevice = event.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _moveDatadisk() {
|
||||||
|
this.moving = true;
|
||||||
|
try {
|
||||||
|
await moveDatadisk(this.hass, this.selectedDevice!);
|
||||||
|
} catch (err) {
|
||||||
|
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.dialogParams!.supervisor.localize(
|
||||||
|
"system.host.failed_to_move"
|
||||||
|
),
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
|
this.closeDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
paper-dropdown-menu {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
ha-circular-progress {
|
||||||
|
display: block;
|
||||||
|
margin: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-hassio-datadisk": HassioDatadiskDialog;
|
||||||
|
}
|
||||||
|
}
|
17
hassio/src/dialogs/datadisk/show-dialog-hassio-datadisk.ts
Normal file
17
hassio/src/dialogs/datadisk/show-dialog-hassio-datadisk.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
|
|
||||||
|
export interface HassioDatatiskDialogParams {
|
||||||
|
supervisor: Supervisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const showHassioDatadiskDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
dialogParams: HassioDatatiskDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-hassio-datadisk",
|
||||||
|
dialogImport: () => import("./dialog-hassio-datadisk"),
|
||||||
|
dialogParams,
|
||||||
|
});
|
||||||
|
};
|
@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiDotsVertical } from "@mdi/js";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
@ -40,8 +39,9 @@ import {
|
|||||||
roundWithOneDecimal,
|
roundWithOneDecimal,
|
||||||
} from "../../../src/util/calculate";
|
} from "../../../src/util/calculate";
|
||||||
import "../components/supervisor-metric";
|
import "../components/supervisor-metric";
|
||||||
import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
|
import { showHassioDatadiskDialog } from "../dialogs/datadisk/show-dialog-hassio-datadisk";
|
||||||
import { showHassioHardwareDialog } from "../dialogs/hardware/show-dialog-hassio-hardware";
|
import { showHassioHardwareDialog } from "../dialogs/hardware/show-dialog-hassio-hardware";
|
||||||
|
import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-host-info")
|
@customElement("hassio-host-info")
|
||||||
@ -180,20 +180,27 @@ class HassioHostInfo extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-button-menu
|
<ha-button-menu corner="BOTTOM_START">
|
||||||
corner="BOTTOM_START"
|
|
||||||
@action=${this._handleMenuAction}
|
|
||||||
>
|
|
||||||
<mwc-icon-button slot="trigger">
|
<mwc-icon-button slot="trigger">
|
||||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
<mwc-list-item>
|
<mwc-list-item @click=${() => this._handleMenuAction("hardware")}>
|
||||||
${this.supervisor.localize("system.host.hardware")}
|
${this.supervisor.localize("system.host.hardware")}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
${this.supervisor.host.features.includes("haos")
|
${this.supervisor.host.features.includes("haos")
|
||||||
? html`<mwc-list-item>
|
? html`<mwc-list-item
|
||||||
|
@click=${() => this._handleMenuAction("import_from_usb")}
|
||||||
|
>
|
||||||
${this.supervisor.localize("system.host.import_from_usb")}
|
${this.supervisor.localize("system.host.import_from_usb")}
|
||||||
|
</mwc-list-item>
|
||||||
|
${this.supervisor.host.features.includes("os_agent") &&
|
||||||
|
atLeastVersion(this.supervisor.host.agent_version, 1, 2, 0)
|
||||||
|
? html`<mwc-list-item
|
||||||
|
@click=${() => this._handleMenuAction("move_datadisk")}
|
||||||
|
>
|
||||||
|
${this.supervisor.localize("system.host.move_datadisk")}
|
||||||
</mwc-list-item>`
|
</mwc-list-item>`
|
||||||
|
: ""} `
|
||||||
: ""}
|
: ""}
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
@ -216,17 +223,26 @@ class HassioHostInfo extends LitElement {
|
|||||||
return network_info.interfaces.find((a) => a.primary)?.ipv4?.address![0];
|
return network_info.interfaces.find((a) => a.primary)?.ipv4?.address![0];
|
||||||
});
|
});
|
||||||
|
|
||||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
private async _handleMenuAction(action: string) {
|
||||||
switch (ev.detail.index) {
|
switch (action) {
|
||||||
case 0:
|
case "hardware":
|
||||||
await this._showHardware();
|
await this._showHardware();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case "import_from_usb":
|
||||||
await this._importFromUSB();
|
await this._importFromUSB();
|
||||||
break;
|
break;
|
||||||
|
case "move_datadisk":
|
||||||
|
await this._moveDatadisk();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _moveDatadisk(): void {
|
||||||
|
showHassioDatadiskDialog(this, {
|
||||||
|
supervisor: this.supervisor,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _showHardware(): Promise<void> {
|
private async _showHardware(): Promise<void> {
|
||||||
let hardware;
|
let hardware;
|
||||||
try {
|
try {
|
||||||
|
@ -3,6 +3,7 @@ import { HomeAssistant } from "../../types";
|
|||||||
import { hassioApiResultExtractor, HassioResponse } from "./common";
|
import { hassioApiResultExtractor, HassioResponse } from "./common";
|
||||||
|
|
||||||
export type HassioHostInfo = {
|
export type HassioHostInfo = {
|
||||||
|
agent_version: string;
|
||||||
chassis: string;
|
chassis: string;
|
||||||
cpe: string;
|
cpe: string;
|
||||||
deployment: string;
|
deployment: string;
|
||||||
@ -14,6 +15,8 @@ export type HassioHostInfo = {
|
|||||||
hostname: string;
|
hostname: string;
|
||||||
kernel: string;
|
kernel: string;
|
||||||
operating_system: string;
|
operating_system: string;
|
||||||
|
boot_timestamp: number;
|
||||||
|
startup_time: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface HassioHassOSInfo {
|
export interface HassioHassOSInfo {
|
||||||
@ -22,6 +25,11 @@ export interface HassioHassOSInfo {
|
|||||||
update_available: boolean;
|
update_available: boolean;
|
||||||
version_latest: string | null;
|
version_latest: string | null;
|
||||||
version: string | null;
|
version: string | null;
|
||||||
|
data_disk: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DatadiskList {
|
||||||
|
devices: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchHassioHostInfo = async (
|
export const fetchHassioHostInfo = async (
|
||||||
@ -129,3 +137,34 @@ export const changeHostOptions = async (hass: HomeAssistant, options: any) => {
|
|||||||
options
|
options
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const moveDatadisk = async (hass: HomeAssistant, device: string) => {
|
||||||
|
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
|
||||||
|
return hass.callWS({
|
||||||
|
type: "supervisor/api",
|
||||||
|
endpoint: "/os/datadisk/move",
|
||||||
|
method: "post",
|
||||||
|
timeout: null,
|
||||||
|
data: { device },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/datadisk/move");
|
||||||
|
};
|
||||||
|
|
||||||
|
export const listDatadisks = async (
|
||||||
|
hass: HomeAssistant
|
||||||
|
): Promise<DatadiskList> => {
|
||||||
|
if (atLeastVersion(hass.config.version, 2021, 2, 4)) {
|
||||||
|
return hass.callWS<DatadiskList>({
|
||||||
|
type: "supervisor/api",
|
||||||
|
endpoint: "/os/datadisk/list",
|
||||||
|
method: "get",
|
||||||
|
timeout: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return hassioApiResultExtractor(
|
||||||
|
await hass.callApi<HassioResponse<DatadiskList>>("GET", "/os/datadisk/list")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -4104,6 +4104,7 @@
|
|||||||
"failed_to_shutdown": "Failed to shutdown the host",
|
"failed_to_shutdown": "Failed to shutdown the host",
|
||||||
"failed_to_set_hostname": "Setting hostname failed",
|
"failed_to_set_hostname": "Setting hostname failed",
|
||||||
"failed_to_import_from_usb": "Failed to import from USB",
|
"failed_to_import_from_usb": "Failed to import from USB",
|
||||||
|
"failed_to_move": "Failed to move datadisk",
|
||||||
"used_space": "Used space",
|
"used_space": "Used space",
|
||||||
"hostname": "Hostname",
|
"hostname": "Hostname",
|
||||||
"change_hostname": "Change Hostname",
|
"change_hostname": "Change Hostname",
|
||||||
@ -4119,7 +4120,8 @@
|
|||||||
"confirm_shutdown": "Are you sure you want to shutdown the host?",
|
"confirm_shutdown": "Are you sure you want to shutdown the host?",
|
||||||
"shutdown_host": "Shutdown host",
|
"shutdown_host": "Shutdown host",
|
||||||
"hardware": "Hardware",
|
"hardware": "Hardware",
|
||||||
"import_from_usb": "Import from USB"
|
"import_from_usb": "Import from USB",
|
||||||
|
"move_datadisk": "Move datadisk"
|
||||||
},
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"cpu_usage": "Core CPU Usage",
|
"cpu_usage": "Core CPU Usage",
|
||||||
@ -4206,6 +4208,17 @@
|
|||||||
"id": "ID",
|
"id": "ID",
|
||||||
"attributes": "Attributes",
|
"attributes": "Attributes",
|
||||||
"device_path": "Device path"
|
"device_path": "Device path"
|
||||||
|
},
|
||||||
|
"datadisk_move": {
|
||||||
|
"title": "[%key:supervisor::system::host::move_datadisk%]",
|
||||||
|
"description": "You are currently using ''{current_path}'' as datadisk. Moving data disks will reboot your device and it's estimated to take {time} minutes. Your Home Assistant installation will not be accessible during this period. Do not disconnect the power during the move!",
|
||||||
|
"select_device": "Select new datadisk",
|
||||||
|
"no_devices": "No suitable attached devices found",
|
||||||
|
"moving_desc": "Rebooting and moving datadisk. Please have patience",
|
||||||
|
"moving": "Moving datadisk",
|
||||||
|
"loading_devices": "Loading devices",
|
||||||
|
"cancel": "[%key:ui::common::cancel%]",
|
||||||
|
"move": "Move"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user