Add Core to supervisor/system (#8123)

This commit is contained in:
Joakim Sørensen 2021-01-11 13:51:52 +01:00 committed by GitHub
parent a4657541fc
commit b1483287dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 620 additions and 357 deletions

View File

@ -0,0 +1,87 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-settings-row";
import { roundWithOneDecimal } from "../../../src/util/calculate";
@customElement("supervisor-metric")
class SupervisorMetric extends LitElement {
@property({ type: Number }) public value!: number;
@property({ type: String }) public description!: string;
@property({ type: String }) public tooltip?: string;
protected render(): TemplateResult {
const roundedValue = roundWithOneDecimal(this.value);
return html`<ha-settings-row>
<span slot="heading">
${this.description}
</span>
<div slot="description" title="${this.tooltip ?? ""}">
<span class="value">
${roundedValue}%
</span>
<ha-bar
class="${classMap({
"target-warning": roundedValue > 50,
"target-critical": roundedValue > 85,
})}"
.value=${this.value}
></ha-bar>
</div>
</ha-settings-row>`;
}
static get styles(): CSSResult {
return css`
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(
--hassio-bar-ok-color,
var(--success-color)
);
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 42px;
padding-right: 4px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"supervisor-metric": SupervisorMetric;
}
}

View File

@ -0,0 +1,246 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
import {
extractApiErrorMessage,
fetchHassioStats,
HassioStats,
} from "../../../src/data/hassio/common";
import { restartCore, updateCore } from "../../../src/data/supervisor/core";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { bytesToString } from "../../../src/util/bytes-to-string";
import "../components/supervisor-metric";
import { hassioStyle } from "../resources/hassio-style";
@customElement("hassio-core-info")
class HassioCoreInfo extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _metrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
{
description: "Core CPU Usage",
value: this._metrics?.cpu_percent,
},
{
description: "Core RAM Usage",
value: this._metrics?.memory_percent,
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
this._metrics?.memory_limit
)}`,
},
];
return html`
<ha-card header="Core">
<div class="card-content">
<div>
<ha-settings-row>
<span slot="heading">
Version
</span>
<span slot="description">
core-${this.supervisor.core.version}
</span>
</ha-settings-row>
<ha-settings-row>
<span slot="heading">
Newest Version
</span>
<span slot="description">
core-${this.supervisor.core.version_latest}
</span>
${this.supervisor.core.update_available
? html`
<ha-progress-button
title="Update the core"
@click=${this._coreUpdate}
>
Update
</ha-progress-button>
`
: ""}
</ha-settings-row>
</div>
<div>
${metrics.map(
(metric) =>
html`
<supervisor-metric
.description=${metric.description}
.value=${metric.value ?? 0}
.tooltip=${metric.tooltip}
></supervisor-metric>
`
)}
</div>
</div>
<div class="card-actions">
<ha-progress-button
slot="primaryAction"
class="warning"
@click=${this._coreRestart}
title="Restart Home Assistant Core"
>
Restart Core
</ha-progress-button>
</div>
</ha-card>
`;
}
protected firstUpdated(): void {
this._loadData();
}
private async _loadData(): Promise<void> {
this._metrics = await fetchHassioStats(this.hass, "core");
}
private async _coreRestart(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: "Restart Home Assistant Core",
text: "Are you sure you want to restart Home Assistant Core",
confirmText: "restart",
dismissText: "cancel",
});
if (!confirmed) {
button.progress = false;
return;
}
try {
await restartCore(this.hass);
} catch (err) {
showAlertDialog(this, {
title: "Failed to restart Home Assistant Core",
text: extractApiErrorMessage(err),
});
} finally {
button.progress = false;
}
}
private async _coreUpdate(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: "Update Home Assistant Core",
text: `Are you sure you want to update Home Assistant Core to version ${this.supervisor.core.version_latest}?`,
confirmText: "update",
dismissText: "cancel",
});
if (!confirmed) {
button.progress = false;
return;
}
try {
await updateCore(this.hass);
} catch (err) {
showAlertDialog(this, {
title: "Failed to update Home Assistant Core",
text: extractApiErrorMessage(err),
});
} finally {
button.progress = false;
}
}
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
css`
ha-card {
height: 100%;
justify-content: space-between;
flex-direction: column;
display: flex;
}
.card-actions {
height: 48px;
border-top: none;
display: flex;
justify-content: flex-end;
align-items: center;
}
.card-content {
display: flex;
flex-direction: column;
height: calc(100% - 124px);
justify-content: space-between;
}
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row[three-line] {
height: 74px;
}
ha-settings-row > span[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
}
.warning {
--mdc-theme-primary: var(--error-color);
}
ha-button-menu {
color: var(--secondary-text-color);
--mdc-menu-min-width: 200px;
}
@media (min-width: 563px) {
paper-listbox {
max-height: 150px;
overflow: auto;
}
}
paper-item {
cursor: pointer;
min-height: 35px;
}
mwc-list-item ha-svg-icon {
color: var(--secondary-text-color);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hassio-core-info": HassioCoreInfo;
}
}

View File

@ -43,6 +43,11 @@ import {
} from "../../../src/dialogs/generic/show-dialog-box"; } from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import {
getValueInPercentage,
roundWithOneDecimal,
} from "../../../src/util/calculate";
import "../components/supervisor-metric";
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown"; import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
import { showNetworkDialog } from "../dialogs/network/show-dialog-network"; import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
@ -57,80 +62,105 @@ class HassioHostInfo extends LitElement {
const primaryIpAddress = this.supervisor.host.features.includes("network") const primaryIpAddress = this.supervisor.host.features.includes("network")
? this._primaryIpAddress(this.supervisor.network!) ? this._primaryIpAddress(this.supervisor.network!)
: ""; : "";
return html`
<ha-card header="Host System">
<div class="card-content">
${this.supervisor.host.features.includes("hostname")
? html`<ha-settings-row>
<span slot="heading">
Hostname
</span>
<span slot="description">
${this.supervisor.host.hostname}
</span>
<mwc-button
title="Change the hostname"
label="Change"
@click=${this._changeHostnameClicked}
>
</mwc-button>
</ha-settings-row>`
: ""}
${this.supervisor.host.features.includes("network")
? html` <ha-settings-row>
<span slot="heading">
IP Address
</span>
<span slot="description">
${primaryIpAddress}
</span>
<mwc-button
title="Change the network"
label="Change"
@click=${this._changeNetworkClicked}
>
</mwc-button>
</ha-settings-row>`
: ""}
<ha-settings-row> const metrics = [
<span slot="heading"> {
Operating System description: "Used Space",
</span> value: this._getUsedSpace(
<span slot="description"> this.supervisor.host.disk_used,
${this.supervisor.host.operating_system} this.supervisor.host.disk_total
</span> ),
${this.supervisor.os.update_available tooltip: `${this.supervisor.host.disk_used} GB/${this.supervisor.host.disk_total} GB`,
? html` },
<ha-progress-button ];
title="Update the host OS" return html`
@click=${this._osUpdate} <ha-card header="Host">
<div class="card-content">
<div>
${this.supervisor.host.features.includes("hostname")
? html`<ha-settings-row>
<span slot="heading">
Hostname
</span>
<span slot="description">
${this.supervisor.host.hostname}
</span>
<mwc-button
title="Change the hostname"
label="Change"
@click=${this._changeHostnameClicked}
> >
Update </mwc-button>
</ha-progress-button> </ha-settings-row>`
`
: ""} : ""}
</ha-settings-row> ${this.supervisor.host.features.includes("network")
${!this.supervisor.host.features.includes("hassos") ? html` <ha-settings-row>
? html`<ha-settings-row> <span slot="heading">
<span slot="heading"> IP Address
Docker version </span>
</span> <span slot="description">
<span slot="description"> ${primaryIpAddress}
${this.supervisor.info.docker} </span>
</span> <mwc-button
</ha-settings-row>` title="Change the network"
: ""} label="Change"
${this.supervisor.host.deployment @click=${this._changeNetworkClicked}
? html`<ha-settings-row> >
<span slot="heading"> </mwc-button>
Deployment </ha-settings-row>`
</span> : ""}
<span slot="description">
${this.supervisor.host.deployment} <ha-settings-row>
</span> <span slot="heading">
</ha-settings-row>` Operating System
: ""} </span>
<span slot="description">
${this.supervisor.host.operating_system}
</span>
${this.supervisor.os.update_available
? html`
<ha-progress-button
title="Update the host OS"
@click=${this._osUpdate}
>
Update
</ha-progress-button>
`
: ""}
</ha-settings-row>
${!this.supervisor.host.features.includes("hassos")
? html`<ha-settings-row>
<span slot="heading">
Docker version
</span>
<span slot="description">
${this.supervisor.info.docker}
</span>
</ha-settings-row>`
: ""}
${this.supervisor.host.deployment
? html`<ha-settings-row>
<span slot="heading">
Deployment
</span>
<span slot="description">
${this.supervisor.host.deployment}
</span>
</ha-settings-row>`
: ""}
</div>
<div>
${metrics.map(
(metric) =>
html`
<supervisor-metric
.description=${metric.description}
.value=${metric.value ?? 0}
.tooltip=${metric.tooltip}
></supervisor-metric>
`
)}
</div>
</div> </div>
<div class="card-actions"> <div class="card-actions">
${this.supervisor.host.features.includes("reboot") ${this.supervisor.host.features.includes("reboot")
@ -140,7 +170,7 @@ class HassioHostInfo extends LitElement {
class="warning" class="warning"
@click=${this._hostReboot} @click=${this._hostReboot}
> >
Reboot Reboot Host
</ha-progress-button> </ha-progress-button>
` `
: ""} : ""}
@ -151,7 +181,7 @@ class HassioHostInfo extends LitElement {
class="warning" class="warning"
@click=${this._hostShutdown} @click=${this._hostShutdown}
> >
Shutdown Shutdown Host
</ha-progress-button> </ha-progress-button>
` `
: ""} : ""}
@ -183,6 +213,10 @@ class HassioHostInfo extends LitElement {
this._loadData(); this._loadData();
} }
private _getUsedSpace = memoizeOne((used: number, total: number) =>
roundWithOneDecimal(getValueInPercentage(used, 0, total))
);
private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => { private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => {
if (!network_info || !network_info.interfaces) { if (!network_info || !network_info.interfaces) {
return ""; return "";
@ -369,6 +403,12 @@ class HassioHostInfo extends LitElement {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.card-content {
display: flex;
flex-direction: column;
height: calc(100% - 124px);
justify-content: space-between;
}
ha-settings-row { ha-settings-row {
padding: 0; padding: 0;
height: 54px; height: 54px;

View File

@ -3,6 +3,7 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
TemplateResult, TemplateResult,
@ -12,7 +13,11 @@ import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row"; import "../../../src/components/ha-settings-row";
import "../../../src/components/ha-switch"; import "../../../src/components/ha-switch";
import { extractApiErrorMessage } from "../../../src/data/hassio/common"; import {
extractApiErrorMessage,
fetchHassioStats,
HassioStats,
} from "../../../src/data/hassio/common";
import { import {
fetchHassioSupervisorInfo, fetchHassioSupervisorInfo,
reloadSupervisor, reloadSupervisor,
@ -28,7 +33,9 @@ import {
} from "../../../src/dialogs/generic/show-dialog-box"; } from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import { bytesToString } from "../../../src/util/bytes-to-string";
import { documentationUrl } from "../../../src/util/documentation-url"; import { documentationUrl } from "../../../src/util/documentation-url";
import "../components/supervisor-metric";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
const UNSUPPORTED_REASON = { const UNSUPPORTED_REASON = {
@ -87,127 +94,164 @@ class HassioSupervisorInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _metrics?: HassioStats;
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
const metrics = [
{
description: "Supervisor CPU Usage",
value: this._metrics?.cpu_percent,
},
{
description: "Supervisor RAM Usage",
value: this._metrics?.memory_percent,
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
this._metrics?.memory_limit
)}`,
},
];
return html` return html`
<ha-card header="Supervisor"> <ha-card header="Supervisor">
<div class="card-content"> <div class="card-content">
<ha-settings-row> <div>
<span slot="heading"> <ha-settings-row>
Version <span slot="heading">
</span> Version
<span slot="description"> </span>
supervisor-${this.supervisor.supervisor.version} <span slot="description">
</span> supervisor-${this.supervisor.supervisor.version}
</ha-settings-row> </span>
<ha-settings-row> </ha-settings-row>
<span slot="heading"> <ha-settings-row>
Newest Version <span slot="heading">
</span> Newest Version
<span slot="description"> </span>
supervisor-${this.supervisor.supervisor.version_latest} <span slot="description">
</span> supervisor-${this.supervisor.supervisor.version_latest}
${this.supervisor.supervisor.update_available </span>
? html` ${this.supervisor.supervisor.update_available
<ha-progress-button ? html`
title="Update the supervisor" <ha-progress-button
@click=${this._supervisorUpdate} title="Update the supervisor"
> @click=${this._supervisorUpdate}
Update >
</ha-progress-button> Update
` </ha-progress-button>
: ""} `
</ha-settings-row> : ""}
<ha-settings-row> </ha-settings-row>
<span slot="heading"> <ha-settings-row>
Channel <span slot="heading">
</span> Channel
<span slot="description"> </span>
${this.supervisor.supervisor.channel} <span slot="description">
</span> ${this.supervisor.supervisor.channel}
${this.supervisor.supervisor.channel === "beta" </span>
? html` ${this.supervisor.supervisor.channel === "beta"
<ha-progress-button ? html`
@click=${this._toggleBeta} <ha-progress-button
title="Get stable updates for Home Assistant, supervisor and host" @click=${this._toggleBeta}
> title="Get stable updates for Home Assistant, supervisor and host"
Leave beta channel >
</ha-progress-button> Leave beta channel
` </ha-progress-button>
: this.supervisor.supervisor.channel === "stable" `
? html` : this.supervisor.supervisor.channel === "stable"
<ha-progress-button ? html`
@click=${this._toggleBeta} <ha-progress-button
title="Get beta updates for Home Assistant (RCs), supervisor and host" @click=${this._toggleBeta}
> title="Get beta updates for Home Assistant (RCs), supervisor and host"
Join beta channel >
</ha-progress-button> Join beta channel
` </ha-progress-button>
: ""} `
</ha-settings-row> : ""}
</ha-settings-row>
${this.supervisor.supervisor.supported ${this.supervisor.supervisor.supported
? html` <ha-settings-row three-line> ? html` <ha-settings-row three-line>
<span slot="heading"> <span slot="heading">
Share Diagnostics Share Diagnostics
</span> </span>
<div slot="description" class="diagnostics-description"> <div slot="description" class="diagnostics-description">
Share crash reports and diagnostic information. Share crash reports and diagnostic information.
<button
class="link"
title="Show more information about this"
@click=${this._diagnosticsInformationDialog}
>
Learn more
</button>
</div>
<ha-switch
haptic
.checked=${this.supervisor.supervisor.diagnostics}
@change=${this._toggleDiagnostics}
></ha-switch>
</ha-settings-row>`
: html`<div class="error">
You are running an unsupported installation.
<button <button
class="link" class="link"
title="Show more information about this" title="Learn more about how you can make your system compliant"
@click=${this._diagnosticsInformationDialog} @click=${this._unsupportedDialog}
> >
Learn more Learn more
</button> </button>
</div> </div>`}
<ha-switch ${!this.supervisor.supervisor.healthy
haptic ? html`<div class="error">
.checked=${this.supervisor.supervisor.diagnostics} Your installation is running in an unhealthy state.
@change=${this._toggleDiagnostics} <button
></ha-switch> class="link"
</ha-settings-row>` title="Learn more about why your system is marked as unhealthy"
: html`<div class="error"> @click=${this._unhealthyDialog}
You are running an unsupported installation. >
<button Learn more
class="link" </button>
title="Learn more about how you can make your system compliant" </div>`
@click=${this._unsupportedDialog} : ""}
> </div>
Learn more <div class="metrics-block">
</button> ${metrics.map(
</div>`} (metric) =>
${!this.supervisor.supervisor.healthy html`
? html`<div class="error"> <supervisor-metric
Your installation is running in an unhealthy state. .description=${metric.description}
<button .value=${metric.value ?? 0}
class="link" .tooltip=${metric.tooltip}
title="Learn more about why your system is marked as unhealthy" ></supervisor-metric>
@click=${this._unhealthyDialog} `
> )}
Learn more </div>
</button>
</div>`
: ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-progress-button <ha-progress-button
@click=${this._supervisorReload} @click=${this._supervisorReload}
title="Reload parts of the Supervisor" title="Reload parts of the Supervisor"
> >
Reload Reload Supervisor
</ha-progress-button> </ha-progress-button>
<ha-progress-button <ha-progress-button
class="warning" class="warning"
@click=${this._supervisorRestart} @click=${this._supervisorRestart}
title="Restart the Supervisor" title="Restart the Supervisor"
> >
Restart Restart Supervisor
</ha-progress-button> </ha-progress-button>
</div> </div>
</ha-card> </ha-card>
`; `;
} }
protected firstUpdated(): void {
this._loadData();
}
private async _loadData(): Promise<void> {
this._metrics = await fetchHassioStats(this.hass, "supervisor");
}
private async _toggleBeta(ev: CustomEvent): Promise<void> { private async _toggleBeta(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any; const button = ev.currentTarget as any;
button.progress = true; button.progress = true;
@ -282,6 +326,18 @@ class HassioSupervisorInfo extends LitElement {
const button = ev.currentTarget as any; const button = ev.currentTarget as any;
button.progress = true; button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: "Restart the Supervisor",
text: "Are you sure you want to restart the Supervisor",
confirmText: "restart",
dismissText: "cancel",
});
if (!confirmed) {
button.progress = false;
return;
}
try { try {
await restartSupervisor(this.hass); await restartSupervisor(this.hass);
} catch (err) { } catch (err) {
@ -426,6 +482,15 @@ class HassioSupervisorInfo extends LitElement {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.card-content {
display: flex;
flex-direction: column;
height: calc(100% - 124px);
justify-content: space-between;
}
.metrics-block {
margin-top: 16px;
}
button.link { button.link {
color: var(--primary-color); color: var(--primary-color);
} }

View File

@ -1,185 +0,0 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import memoizeOne from "memoize-one";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
import { fetchHassioStats, HassioStats } from "../../../src/data/hassio/common";
import { HassioHostInfo } from "../../../src/data/hassio/host";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { bytesToString } from "../../../src/util/bytes-to-string";
import {
getValueInPercentage,
roundWithOneDecimal,
} from "../../../src/util/calculate";
import { hassioStyle } from "../resources/hassio-style";
@customElement("hassio-system-metrics")
class HassioSystemMetrics extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _supervisorMetrics?: HassioStats;
@internalProperty() private _coreMetrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
{
description: "Core CPU Usage",
value: this._coreMetrics?.cpu_percent,
},
{
description: "Core RAM Usage",
value: this._coreMetrics?.memory_percent,
tooltip: `${bytesToString(
this._coreMetrics?.memory_usage
)}/${bytesToString(this._coreMetrics?.memory_limit)}`,
},
{
description: "Supervisor CPU Usage",
value: this._supervisorMetrics?.cpu_percent,
},
{
description: "Supervisor RAM Usage",
value: this._supervisorMetrics?.memory_percent,
tooltip: `${bytesToString(
this._supervisorMetrics?.memory_usage
)}/${bytesToString(this._supervisorMetrics?.memory_limit)}`,
},
{
description: "Used Space",
value: this._getUsedSpace(this.supervisor.host),
tooltip: `${this.supervisor.host.disk_used} GB/${this.supervisor.host.disk_total} GB`,
},
];
return html`
<ha-card header="System Metrics">
<div class="card-content">
${metrics.map((metric) =>
this._renderMetric(
metric.description,
metric.value ?? 0,
metric.tooltip
)
)}
</div>
</ha-card>
`;
}
protected firstUpdated(): void {
this._loadData();
}
private _renderMetric(
description: string,
value: number,
tooltip?: string
): TemplateResult {
const roundedValue = roundWithOneDecimal(value);
return html`<ha-settings-row>
<span slot="heading">
${description}
</span>
<div slot="description" title="${tooltip ?? ""}">
<span class="value">
${roundedValue}%
</span>
<ha-bar
class="${classMap({
"target-warning": roundedValue > 50,
"target-critical": roundedValue > 85,
})}"
.value=${value}
></ha-bar>
</div>
</ha-settings-row>`;
}
private _getUsedSpace = memoizeOne((hostInfo: HassioHostInfo) =>
roundWithOneDecimal(
getValueInPercentage(hostInfo.disk_used, 0, hostInfo.disk_total)
)
);
private async _loadData(): Promise<void> {
const [supervisor, core] = await Promise.all([
fetchHassioStats(this.hass, "supervisor"),
fetchHassioStats(this.hass, "core"),
]);
this._supervisorMetrics = supervisor;
this._coreMetrics = core;
}
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
css`
ha-card {
height: 100%;
justify-content: space-between;
flex-direction: column;
display: flex;
}
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(
--hassio-bar-ok-color,
var(--success-color)
);
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 42px;
padding-right: 4px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hassio-system-metrics": HassioSystemMetrics;
}
}

View File

@ -13,10 +13,10 @@ import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs"; import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
import "./hassio-core-info";
import "./hassio-host-info"; import "./hassio-host-info";
import "./hassio-supervisor-info"; import "./hassio-supervisor-info";
import "./hassio-supervisor-log"; import "./hassio-supervisor-log";
import "./hassio-system-metrics";
@customElement("hassio-system") @customElement("hassio-system")
class HassioSystem extends LitElement { class HassioSystem extends LitElement {
@ -41,6 +41,10 @@ class HassioSystem extends LitElement {
<span slot="header">System</span> <span slot="header">System</span>
<div class="content"> <div class="content">
<div class="card-group"> <div class="card-group">
<hassio-core-info
.hass=${this.hass}
.supervisor=${this.supervisor}
></hassio-core-info>
<hassio-supervisor-info <hassio-supervisor-info
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
@ -49,10 +53,6 @@ class HassioSystem extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
></hassio-host-info> ></hassio-host-info>
<hassio-system-metrics
.hass=${this.hass}
.supervisor=${this.supervisor}
></hassio-system-metrics>
</div> </div>
<hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log> <hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log>
</div> </div>

View File

@ -0,0 +1,10 @@
import { HomeAssistant } from "../../types";
import { HassioResponse } from "../hassio/common";
export const restartCore = async (hass: HomeAssistant) => {
await hass.callService("homeassistant", "restart");
};
export const updateCore = async (hass: HomeAssistant) => {
await hass.callApi<HassioResponse<void>>("POST", `hassio/core/update`);
};