From bd511887a783512a727f9dfd33dc20ca9d8d9bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 22 Sep 2020 21:58:31 +0200 Subject: [PATCH] Adds hassio-system-metrics (#7105) --- hassio/src/system/hassio-system-metrics.ts | 168 +++++++++++++++++++++ hassio/src/system/hassio-system.ts | 5 + src/data/hassio/common.ts | 25 +++ 3 files changed, 198 insertions(+) create mode 100644 hassio/src/system/hassio-system-metrics.ts diff --git a/hassio/src/system/hassio-system-metrics.ts b/hassio/src/system/hassio-system-metrics.ts new file mode 100644 index 0000000000..e201adfc80 --- /dev/null +++ b/hassio/src/system/hassio-system-metrics.ts @@ -0,0 +1,168 @@ +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 { haStyle } from "../../../src/resources/styles"; +import { HomeAssistant } from "../../../src/types"; +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() public hostInfo!: HassioHostInfo; + + @internalProperty() private _supervisorMetrics?: HassioStats; + + @internalProperty() private _coreMetrics?: HassioStats; + + protected render(): TemplateResult | void { + const usedSpace = this._getUsedSpace(this.hostInfo); + const metrics = [ + { + description: "Core CPU usage", + value: this._coreMetrics?.cpu_percent, + }, + { + description: "Core RAM usage", + value: this._coreMetrics?.memory_percent, + }, + { + description: "Supervisor CPU usage", + value: this._supervisorMetrics?.cpu_percent, + }, + { + description: "Supervisor RAM usage", + value: this._supervisorMetrics?.memory_percent, + }, + { + description: "Used space", + value: usedSpace, + }, + ]; + + return html` + +
+ ${metrics.map((metric) => + this._renderMetric(metric.description, metric.value ?? 0) + )} +
+
+ `; + } + + protected firstUpdated(): void { + this._loadData(); + } + + private _renderMetric(description: string, value: number): TemplateResult { + const roundedValue = roundWithOneDecimal(value); + return html` + + ${description} + +
+ + ${roundedValue}% + + 50, + "target-critical": roundedValue > 85, + })}" + .value=${value} + > +
+
`; + } + + private _getUsedSpace = memoizeOne((hostInfo: HassioHostInfo) => + roundWithOneDecimal( + getValueInPercentage(hostInfo.disk_used, 0, hostInfo.disk_total) + ) + ); + + private async _loadData(): Promise { + 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; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hassio-system-metrics": HassioSystemMetrics; + } +} diff --git a/hassio/src/system/hassio-system.ts b/hassio/src/system/hassio-system.ts index f2ffc2cc62..e7eef9eaaa 100644 --- a/hassio/src/system/hassio-system.ts +++ b/hassio/src/system/hassio-system.ts @@ -23,6 +23,7 @@ import { hassioStyle } from "../resources/hassio-style"; import "./hassio-host-info"; import "./hassio-supervisor-info"; import "./hassio-supervisor-log"; +import "./hassio-system-metrics"; @customElement("hassio-system") class HassioSystem extends LitElement { @@ -64,6 +65,10 @@ class HassioSystem extends LitElement { .hostInfo=${this.hostInfo} .hassOsInfo=${this.hassOsInfo} > + diff --git a/src/data/hassio/common.ts b/src/data/hassio/common.ts index ad315096a3..103f61c35a 100644 --- a/src/data/hassio/common.ts +++ b/src/data/hassio/common.ts @@ -1,8 +1,21 @@ +import { HomeAssistant } from "../../types"; + export interface HassioResponse { data: T; result: "ok"; } +export interface HassioStats { + blk_read: number; + blk_write: number; + cpu_percent: number; + memory_limit: number; + memory_percent: number; + memory_usage: number; + network_rx: number; + network_tx: number; +} + export const hassioApiResultExtractor = (response: HassioResponse) => response.data; @@ -15,3 +28,15 @@ export const extractApiErrorMessage = (error: any): string => { }; export const ignoredStatusCodes = new Set([502, 503, 504]); + +export const fetchHassioStats = async ( + hass: HomeAssistant, + container: string +): Promise => { + return hassioApiResultExtractor( + await hass.callApi>( + "GET", + `hassio/${container}/stats` + ) + ); +};