Files
frontend/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
Stefan Agner 92cf8b5579 Remove eMMC specific references in disk life time handling (#26379)
* Remove eMMC specific references in disk life time handling

Remove eMMC specific calculations and references in the disk life
time handling to generalize the code for all disk types. This includes
updating translations and UI components to reflect a more generic
approach to disk life time metrics.

* Assume 30 MB/s as the speed for disk operations

The previous code tried to estimate based on disk type, 30 MB/s for
eMMC devices and 10 MB/s for others. However, this did not work
correctly since the disk_life_time returns null for non-eMMC devices,
leading to 30 MB/s being used for all devices.

Now disk_life_time is not a eMMC indicator anymore. Simply assume a
constant speed of 30 MB/s for all disk operations explicitly.
2025-08-05 10:42:42 +02:00

185 lines
5.9 KiB
TypeScript

import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } 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-dialog";
import "../../../../src/components/ha-button";
import "../../../../src/components/ha-list-item";
import "../../../../src/components/ha-select";
import "../../../../src/components/ha-spinner";
import {
extractApiErrorMessage,
ignoreSupervisorError,
} from "../../../../src/data/hassio/common";
import type { DatadiskList } from "../../../../src/data/hassio/host";
import { listDatadisks, moveDatadisk } from "../../../../src/data/hassio/host";
import type { Supervisor } from "../../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
import type { HomeAssistant } from "../../../../src/types";
import type { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
// Assume a speed of 30 MB/s.
const moveTime = (supervisor.host.disk_used * 1000) / 60 / 30;
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() {
if (!this.dialogParams) {
return nothing;
}
return html`
<ha-dialog
open
scrimClickAction
escapeKeyAction
.heading=${this.moving
? this.dialogParams.supervisor.localize("dialog.datadisk_move.moving")
: this.dialogParams.supervisor.localize("dialog.datadisk_move.title")}
@closed=${this.closeDialog}
?hideActions=${this.moving}
>
${this.moving
? html`<ha-spinner aria-label="Moving" size="large"></ha-spinner>
<p class="progress-text">
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.moving_desc"
)}
</p>`
: html` ${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 />
<ha-select
.label=${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.select_device"
)}
@selected=${this._selectDevice}
dialogInitialFocus
>
${this.devices.map(
(device) =>
html`<ha-list-item .value=${device}
>${device}</ha-list-item
>`
)}
</ha-select>
`
: this.devices === undefined
? this.dialogParams.supervisor.localize(
"dialog.datadisk_move.loading_devices"
)
: this.dialogParams.supervisor.localize(
"dialog.datadisk_move.no_devices"
)}
<ha-button
appearance="plain"
slot="primaryAction"
@click=${this.closeDialog}
dialogInitialFocus
>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.cancel"
)}
</ha-button>
<ha-button
.disabled=${!this.selectedDevice}
slot="primaryAction"
@click=${this._moveDatadisk}
>
${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.move"
)}
</ha-button>`}
</ha-dialog>
`;
}
private _selectDevice(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.dialogParams!.supervisor.localize(
"system.host.failed_to_move"
),
text: extractApiErrorMessage(err),
});
this.closeDialog();
}
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-select {
width: 100%;
}
ha-spinner {
display: block;
margin: 32px;
text-align: center;
}
.progress-text {
text-align: center;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-hassio-datadisk": HassioDatadiskDialog;
}
}