Add hostname and metrics to Add-ons (#8253)

This commit is contained in:
Joakim Sørensen 2021-01-27 22:38:07 +01:00 committed by GitHub
parent 69d0a22091
commit 5ee0250ba5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 324 additions and 238 deletions

View File

@ -49,14 +49,20 @@ import {
uninstallHassioAddon, uninstallHassioAddon,
validateHassioAddonOption, validateHassioAddonOption,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import {
extractApiErrorMessage,
fetchHassioStats,
HassioStats,
} from "../../../../src/data/hassio/common";
import { import {
showAlertDialog, showAlertDialog,
showConfirmationDialog, showConfirmationDialog,
} 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 "../../components/hassio-card-content"; import "../../components/hassio-card-content";
import "../../components/supervisor-metric";
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown"; import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
@ -131,9 +137,24 @@ class HassioAddonInfo extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _metrics?: HassioStats;
@internalProperty() private _error?: string; @internalProperty() private _error?: string;
protected render(): TemplateResult { protected render(): TemplateResult {
const metrics = [
{
description: "Add-on CPU Usage",
value: this._metrics?.cpu_percent,
},
{
description: "Add-on RAM Usage",
value: this._metrics?.memory_percent,
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
this._metrics?.memory_limit
)}`,
},
];
return html` return html`
${this.addon.update_available ${this.addon.update_available
? html` ? html`
@ -237,249 +258,281 @@ class HassioAddonInfo extends LitElement {
> >
for details. for details.
</div> </div>
${this.addon.logo <div class="addon-container">
? html` <div>
<img ${this.addon.logo
class="logo" ? html`
src="/api/hassio/addons/${this.addon.slug}/logo" <img
/> class="logo"
` src="/api/hassio/addons/${this.addon.slug}/logo"
: ""} />
<div class="security"> `
${this.addon.stage !== "stable" : ""}
? html` <ha-label-badge <div class="security">
${this.addon.stage !== "stable"
? html` <ha-label-badge
class=${classMap({
yellow: this.addon.stage === "experimental",
red: this.addon.stage === "deprecated",
})}
@click=${this._showMoreInfo}
id="stage"
label="stage"
description=""
>
<ha-svg-icon
.path=${STAGE_ICON[this.addon.stage]}
></ha-svg-icon>
</ha-label-badge>`
: ""}
<ha-label-badge
class=${classMap({ class=${classMap({
yellow: this.addon.stage === "experimental", green: [5, 6].includes(Number(this.addon.rating)),
red: this.addon.stage === "deprecated", yellow: [3, 4].includes(Number(this.addon.rating)),
red: [1, 2].includes(Number(this.addon.rating)),
})} })}
@click=${this._showMoreInfo} @click=${this._showMoreInfo}
id="stage" id="rating"
label="stage" .value=${this.addon.rating}
label="rating"
description="" description=""
> ></ha-label-badge>
<ha-svg-icon ${this.addon.host_network
.path=${STAGE_ICON[this.addon.stage]} ? html`
></ha-svg-icon> <ha-label-badge
</ha-label-badge>` @click=${this._showMoreInfo}
: ""} id="host_network"
label="host"
description=""
>
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.full_access
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="full_access"
label="hardware"
description=""
>
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.homeassistant_api
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="homeassistant_api"
label="hass"
description=""
>
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this._computeHassioApi
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="hassio_api"
label="hassio"
.description=${this.addon.hassio_role}
>
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.docker_api
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="docker_api"
label="docker"
description=""
>
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.host_pid
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="host_pid"
label="host pid"
description=""
>
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.apparmor
? html`
<ha-label-badge
@click=${this._showMoreInfo}
class=${this._computeApparmorClassName}
id="apparmor"
label="apparmor"
description=""
>
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.auth_api
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="auth_api"
label="auth"
description=""
>
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.ingress
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="ingress"
label="ingress"
description=""
>
<ha-svg-icon
.path=${mdiCursorDefaultClickOutline}
></ha-svg-icon>
</ha-label-badge>
`
: ""}
</div>
<ha-label-badge ${this.addon.version
class=${classMap({ ? html`
green: [5, 6].includes(Number(this.addon.rating)), <div
yellow: [3, 4].includes(Number(this.addon.rating)), class="${classMap({
red: [1, 2].includes(Number(this.addon.rating)), "addon-options": true,
})} started: this.addon.state === "started",
@click=${this._showMoreInfo} })}"
id="rating" >
.value=${this.addon.rating} <ha-settings-row ?three-line=${this.narrow}>
label="rating" <span slot="heading">
description="" Start on boot
></ha-label-badge> </span>
${this.addon.host_network <span slot="description">
? html` Make the add-on start during a system boot
<ha-label-badge </span>
@click=${this._showMoreInfo} <ha-switch
id="host_network" @change=${this._startOnBootToggled}
label="host" .checked=${this.addon.boot === "auto"}
description="" haptic
> ></ha-switch>
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon> </ha-settings-row>
</ha-label-badge>
` ${this.addon.startup !== "once"
: ""} ? html`
${this.addon.full_access <ha-settings-row ?three-line=${this.narrow}>
? html` <span slot="heading">
<ha-label-badge Watchdog
@click=${this._showMoreInfo} </span>
id="full_access" <span slot="description">
label="hardware" This will start the add-on if it crashes
description="" </span>
> <ha-switch
<ha-svg-icon .path=${mdiChip}></ha-svg-icon> @change=${this._watchdogToggled}
</ha-label-badge> .checked=${this.addon.watchdog}
` haptic
: ""} ></ha-switch>
${this.addon.homeassistant_api </ha-settings-row>
? html` `
<ha-label-badge : ""}
@click=${this._showMoreInfo} ${this.addon.auto_update ||
id="homeassistant_api" this.hass.userData?.showAdvanced
label="hass" ? html`
description="" <ha-settings-row ?three-line=${this.narrow}>
> <span slot="heading">
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon> Auto update
</ha-label-badge> </span>
` <span slot="description">
: ""} Auto update the add-on when there is a new
${this._computeHassioApi version available
? html` </span>
<ha-label-badge <ha-switch
@click=${this._showMoreInfo} @change=${this._autoUpdateToggled}
id="hassio_api" .checked=${this.addon.auto_update}
label="hassio" haptic
.description=${this.addon.hassio_role} ></ha-switch>
> </ha-settings-row>
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon> `
</ha-label-badge> : ""}
` ${this.addon.ingress
: ""} ? html`
${this.addon.docker_api <ha-settings-row ?three-line=${this.narrow}>
? html` <span slot="heading">
<ha-label-badge Show in sidebar
@click=${this._showMoreInfo} </span>
id="docker_api" <span slot="description">
label="docker" ${this._computeCannotIngressSidebar
description="" ? "This option requires Home Assistant 0.92 or later."
> : "Add this add-on to your sidebar"}
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon> </span>
</ha-label-badge> <ha-switch
` @change=${this._panelToggled}
: ""} .checked=${this.addon.ingress_panel}
${this.addon.host_pid .disabled=${this._computeCannotIngressSidebar}
? html` haptic
<ha-label-badge ></ha-switch>
@click=${this._showMoreInfo} </ha-settings-row>
id="host_pid" `
label="host pid" : ""}
description="" ${this._computeUsesProtectedOptions
> ? html`
<ha-svg-icon .path=${mdiPound}></ha-svg-icon> <ha-settings-row ?three-line=${this.narrow}>
</ha-label-badge> <span slot="heading">
` Protection mode
: ""} </span>
${this.addon.apparmor <span slot="description">
? html` Blocks elevated system access from the add-on
<ha-label-badge </span>
@click=${this._showMoreInfo} <ha-switch
class=${this._computeApparmorClassName} @change=${this._protectionToggled}
id="apparmor" .checked=${this.addon.protected}
label="apparmor" haptic
description="" ></ha-switch>
> </ha-settings-row>
<ha-svg-icon .path=${mdiShield}></ha-svg-icon> `
</ha-label-badge> : ""}
` </div>
: ""} `
${this.addon.auth_api : ""}
? html` </div>
<ha-label-badge <div>
@click=${this._showMoreInfo} ${this.addon.state === "started"
id="auth_api" ? html`<ha-settings-row ?three-line=${this.narrow}>
label="auth" <span slot="heading">
description="" Hostname
> </span>
<ha-svg-icon .path=${mdiKey}></ha-svg-icon> <code slot="description">
</ha-label-badge> ${this.addon.hostname}
` </code>
: ""} </ha-settings-row>
${this.addon.ingress ${metrics.map(
? html` (metric) =>
<ha-label-badge html`
@click=${this._showMoreInfo} <supervisor-metric
id="ingress" .description=${metric.description}
label="ingress" .value=${metric.value ?? 0}
description="" .tooltip=${metric.tooltip}
> ></supervisor-metric>
<ha-svg-icon `
.path=${mdiCursorDefaultClickOutline} )}`
></ha-svg-icon> : ""}
</ha-label-badge> </div>
`
: ""}
</div> </div>
${this.addon.version
? html`
<div class="addon-options">
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Start on boot
</span>
<span slot="description">
Make the add-on start during a system boot
</span>
<ha-switch
@change=${this._startOnBootToggled}
.checked=${this.addon.boot === "auto"}
haptic
></ha-switch>
</ha-settings-row>
${this.addon.startup !== "once"
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Watchdog
</span>
<span slot="description">
This will start the add-on if it crashes
</span>
<ha-switch
@change=${this._watchdogToggled}
.checked=${this.addon.watchdog}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this.addon.auto_update || this.hass.userData?.showAdvanced
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Auto update
</span>
<span slot="description">
Auto update the add-on when there is a new version
available
</span>
<ha-switch
@change=${this._autoUpdateToggled}
.checked=${this.addon.auto_update}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this.addon.ingress
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Show in sidebar
</span>
<span slot="description">
${this._computeCannotIngressSidebar
? "This option requires Home Assistant 0.92 or later."
: "Add this add-on to your sidebar"}
</span>
<ha-switch
@change=${this._panelToggled}
.checked=${this.addon.ingress_panel}
.disabled=${this._computeCannotIngressSidebar}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this._computeUsesProtectedOptions
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Protection mode
</span>
<span slot="description">
Blocks elevated system access from the add-on
</span>
<ha-switch
@change=${this._protectionToggled}
.checked=${this.addon.protected}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
</div>
`
: ""}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
@ -579,6 +632,22 @@ class HassioAddonInfo extends LitElement {
`; `;
} }
protected updated(changedProps) {
super.updated(changedProps);
if (changedProps.has("addon")) {
this._loadData();
}
}
private async _loadData(): Promise<void> {
if (this.addon.state === "started") {
this._metrics = await fetchHassioStats(
this.hass,
`addons/${this.addon.slug}`
);
}
}
private get _computeHassioApi(): boolean { private get _computeHassioApi(): boolean {
return ( return (
this.addon.hassio_api && this.addon.hassio_api &&
@ -988,10 +1057,26 @@ class HassioAddonInfo extends LitElement {
.addon-options { .addon-options {
max-width: 50%; max-width: 50%;
} }
.addon-options.started {
max-width: 90%;
}
.addon-container {
display: grid;
grid-auto-flow: column;
grid-template-columns: 1fr auto;
}
.addon-container div:last-of-type {
align-self: end;
}
@media (max-width: 720px) { @media (max-width: 720px) {
.addon-options { .addon-options {
max-width: 100%; max-width: 100%;
} }
.addon-container {
display: block;
}
} }
`, `,
]; ];

View File

@ -41,6 +41,7 @@ export interface HassioAddonDetails extends HassioAddonInfo {
gpio: boolean; gpio: boolean;
hassio_api: boolean; hassio_api: boolean;
hassio_role: "default" | "homeassistant" | "manager" | "admin"; hassio_role: "default" | "homeassistant" | "manager" | "admin";
hostname: string;
homeassistant_api: boolean; homeassistant_api: boolean;
homeassistant: string; homeassistant: string;
host_dbus: boolean; host_dbus: boolean;