Move Data Disk Moving to Storage (#12416)

This commit is contained in:
Zack Barett 2022-04-25 13:49:44 -05:00 committed by GitHub
parent e927091d21
commit 1faf60444d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 291 additions and 34 deletions

View File

@ -444,7 +444,7 @@ class HaPanelConfig extends HassRouterPage {
},
storage: {
tag: "ha-config-section-storage",
load: () => import("./core/ha-config-section-storage"),
load: () => import("./storage/ha-config-section-storage"),
},
system_health: {
tag: "ha-config-system-health",

View File

@ -0,0 +1,200 @@
import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-circular-progress";
import "../../../components/ha-markdown";
import "../../../components/ha-select";
import {
extractApiErrorMessage,
ignoreSupervisorError,
} from "../../../data/hassio/common";
import {
DatadiskList,
fetchHassioHassOsInfo,
HassioHassOSInfo,
HassioHostInfo,
listDatadisks,
moveDatadisk,
} from "../../../data/hassio/host";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { haStyle, haStyleDialog } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { MoveDatadiskDialogParams } from "./show-dialog-move-datadisk";
const calculateMoveTime = memoizeOne((hostInfo: HassioHostInfo): number => {
const speed = hostInfo.disk_life_time !== "" ? 30 : 10;
const moveTime = (hostInfo.disk_used * 1000) / 60 / speed;
const rebootTime = (hostInfo.startup_time * 4) / 60;
return Math.ceil((moveTime + rebootTime) / 10) * 10;
});
@customElement("dialog-move-datadisk")
class MoveDatadiskDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _hostInfo?: HassioHostInfo;
@state() private _selectedDevice?: string;
@state() private _devices?: DatadiskList["devices"];
@state() private _osInfo?: HassioHassOSInfo;
@state() private _moving = false;
public async showDialog(
dialogParams: MoveDatadiskDialogParams
): Promise<Promise<void>> {
this._hostInfo = dialogParams.hostInfo;
try {
this._osInfo = await fetchHassioHassOsInfo(this.hass);
} catch (err: any) {
await showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.hardware.available_hardware.failed_to_get"
),
text: extractApiErrorMessage(err),
});
}
listDatadisks(this.hass).then((data) => {
this._devices = data.devices;
});
}
public closeDialog(): void {
this._selectedDevice = undefined;
this._devices = undefined;
this._moving = false;
this._hostInfo = undefined;
this._osInfo = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this._hostInfo || !this._osInfo) {
return html``;
}
return html`
<ha-dialog
open
scrimClickAction
escapeKeyAction
.heading=${this._moving
? this.hass.localize("ui.panel.config.storage.datadisk.moving")
: this.hass.localize("ui.panel.config.storage.datadisk.title")}
@closed=${this.closeDialog}
?hideActions=${this._moving}
>
${this._moving
? html` <ha-circular-progress alt="Moving" size="large" active>
</ha-circular-progress>
<p class="progress-text">
${this.hass.localize(
"ui.panel.config.storage.datadisk.moving_desc"
)}
</p>`
: html` ${this._devices?.length
? html`
${this.hass.localize(
"ui.panel.config.storage.datadisk.description",
{
current_path: this._osInfo.data_disk,
time: calculateMoveTime(this._hostInfo),
}
)}
<br /><br />
<ha-select
.label=${this.hass.localize(
"ui.panel.config.storage.datadisk.select_device"
)}
@selected=${this._select_device}
dialogInitialFocus
>
${this._devices.map(
(device) =>
html`<mwc-list-item .value=${device}
>${device}</mwc-list-item
>`
)}
</ha-select>
`
: this._devices === undefined
? this.hass.localize(
"ui.panel.config.storage.datadisk.loading_devices"
)
: this.hass.localize(
"ui.panel.config.storage.datadisk.no_devices"
)}
<mwc-button
slot="secondaryAction"
@click=${this.closeDialog}
dialogInitialFocus
>
${this.hass.localize("ui.panel.config.storage.datadisk.cancel")}
</mwc-button>
<mwc-button
.disabled=${!this._selectedDevice}
slot="primaryAction"
@click=${this._moveDatadisk}
>
${this.hass.localize("ui.panel.config.storage.datadisk.move")}
</mwc-button>`}
</ha-dialog>
`;
}
private _select_device(ev) {
this._selectedDevice = ev.target.value;
}
private async _moveDatadisk() {
this._moving = true;
try {
await moveDatadisk(this.hass, this._selectedDevice!);
} catch (err: any) {
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.storage.datadisk.failed_to_move"
),
text: extractApiErrorMessage(err),
});
this.closeDialog();
}
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-select {
width: 100%;
}
ha-circular-progress {
display: block;
margin: 32px;
text-align: center;
}
.progress-text {
text-align: center;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-move-datadisk": MoveDatadiskDialog;
}
}

View File

@ -2,7 +2,6 @@ import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/ha-alert";
import "../../../components/ha-bar";
import "../../../components/ha-metric";
import { fetchHassioHostInfo, HassioHostInfo } from "../../../data/hassio/host";
import "../../../layouts/hass-subpage";
@ -11,7 +10,8 @@ import {
getValueInPercentage,
roundWithOneDecimal,
} from "../../../util/calculate";
import "./ha-config-analytics";
import "../core/ha-config-analytics";
import { showMoveDatadiskDialog } from "./show-dialog-move-datadisk";
@customElement("ha-config-section-storage")
class HaConfigSectionStorage extends LitElement {
@ -23,7 +23,7 @@ class HaConfigSectionStorage extends LitElement {
@state() private _error?: { code: string; message: string };
@state() private _storageData?: HassioHostInfo;
@state() private _hostInfo?: HassioHostInfo;
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
@ -48,35 +48,44 @@ class HaConfigSectionStorage extends LitElement {
>
`
: ""}
${this._storageData
${this._hostInfo
? html`
<ha-card outlined>
<div class="card-content">
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.storage.used_space"
)}
.value=${this._getUsedSpace(
this._storageData?.disk_used,
this._storageData?.disk_total
this._hostInfo?.disk_used,
this._hostInfo?.disk_total
)}
.tooltip=${`${this._storageData.disk_used} GB/${this._storageData.disk_total} GB`}
.tooltip=${`${this._hostInfo.disk_used} GB/${this._hostInfo.disk_total} GB`}
></ha-metric>
${this._storageData.disk_life_time !== "" &&
this._storageData.disk_life_time >= 10
${this._hostInfo.disk_life_time !== "" &&
this._hostInfo.disk_life_time >= 10
? html`
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.storage.emmc_lifetime_used"
)}
.value=${this._storageData.disk_life_time}
.value=${this._hostInfo.disk_life_time}
.tooltip=${`${
this._storageData.disk_life_time - 10
this._hostInfo.disk_life_time - 10
} % -
${this._storageData.disk_life_time} %`}
${this._hostInfo.disk_life_time} %`}
class="emmc"
></ha-metric>
`
: ""}
</div>
<div class="card-actions">
<mwc-button @click=${this._moveDatadisk}>
${this.hass.localize(
"ui.panel.config.storage.datadisk.title"
)}
</mwc-button>
</div>
</ha-card>
`
: ""}
@ -87,12 +96,18 @@ class HaConfigSectionStorage extends LitElement {
private async _load() {
try {
this._storageData = await fetchHassioHostInfo(this.hass);
this._hostInfo = await fetchHassioHostInfo(this.hass);
} catch (err: any) {
this._error = err.message || err;
}
}
private _moveDatadisk(): void {
showMoveDatadiskDialog(this, {
hostInfo: this._hostInfo!,
});
}
private _getUsedSpace = (used: number, total: number) =>
roundWithOneDecimal(getValueInPercentage(used, 0, total));
@ -103,7 +118,6 @@ class HaConfigSectionStorage extends LitElement {
margin: 0 auto;
}
ha-card {
padding: 16px;
max-width: 500px;
margin: 0 auto;
height: 100%;
@ -111,6 +125,20 @@ class HaConfigSectionStorage extends LitElement {
flex-direction: column;
display: flex;
}
.card-actions {
height: 48px;
border-top: none;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-content {
display: flex;
justify-content: space-between;
flex-direction: column;
padding: 16px 16px 0 16px;
}
`;
}

View File

@ -0,0 +1,17 @@
import { fireEvent } from "../../../common/dom/fire_event";
import { HassioHostInfo } from "../../../data/hassio/host";
export interface MoveDatadiskDialogParams {
hostInfo: HassioHostInfo;
}
export const showMoveDatadiskDialog = (
element: HTMLElement,
dialogParams: MoveDatadiskDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-move-datadisk",
dialogImport: () => import("./dialog-move-datadisk"),
dialogParams,
});
};

View File

@ -3152,7 +3152,19 @@
"storage": {
"caption": "Storage",
"used_space": "Used Space",
"emmc_lifetime_used": "eMMC Lifetime Used"
"emmc_lifetime_used": "eMMC Lifetime Used",
"datadisk": {
"title": "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%]",
"failed_to_move": "Failed to move datadisk",
"move": "Move"
}
},
"system_health": {
"caption": "System Health"