Add support for hardware option flows (#14364)

This commit is contained in:
Bram Kragten 2022-11-28 12:47:27 +01:00 committed by GitHub
parent e6b3475b5b
commit fce87ff0fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 150 additions and 32 deletions

View File

@ -26,7 +26,9 @@ export interface HardwareInfo {
} }
export interface HardwareInfoEntry { export interface HardwareInfoEntry {
board: HardwareInfoBoardInfo; board: HardwareInfoBoardInfo | null;
dongle: HardwareInfoDongleInfo | null;
config_entries: string[];
name: string; name: string;
url?: string; url?: string;
} }
@ -38,6 +40,14 @@ export interface HardwareInfoBoardInfo {
hassio_board_id?: string; hassio_board_id?: string;
} }
export interface HardwareInfoDongleInfo {
manufacturer: string;
description: string;
pid?: string;
vid?: string;
serial_number?: string;
}
export interface SystemStatusStreamMessage { export interface SystemStatusStreamMessage {
cpu_percent: number; cpu_percent: number;
memory_free_mb: number; memory_free_mb: number;

View File

@ -1,7 +1,12 @@
import { LocalizeFunc } from "../common/translations/localize"; import { LocalizeFunc } from "../common/translations/localize";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
export type IntegrationType = "device" | "helper" | "hub" | "service"; export type IntegrationType =
| "device"
| "helper"
| "hub"
| "service"
| "hardware";
export interface IntegrationManifest { export interface IntegrationManifest {
is_built_in: boolean; is_built_in: boolean;

View File

@ -2,6 +2,7 @@ import "@material/mwc-list/mwc-list";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js"; import { mdiDotsVertical } from "@mdi/js";
import type { ChartOptions } from "chart.js"; import type { ChartOptions } from "chart.js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined"; import { ifDefined } from "lit/directives/if-defined";
@ -16,6 +17,10 @@ import "../../../components/ha-card";
import "../../../components/ha-clickable-list-item"; import "../../../components/ha-clickable-list-item";
import "../../../components/ha-icon-next"; import "../../../components/ha-icon-next";
import "../../../components/ha-settings-row"; import "../../../components/ha-settings-row";
import {
ConfigEntry,
subscribeConfigEntries,
} from "../../../data/config_entries";
import { import {
BOARD_NAMES, BOARD_NAMES,
HardwareInfo, HardwareInfo,
@ -33,6 +38,7 @@ import {
rebootHost, rebootHost,
shutdownHost, shutdownHost,
} from "../../../data/hassio/host"; } from "../../../data/hassio/host";
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
import { import {
showAlertDialog, showAlertDialog,
showConfirmationDialog, showConfirmationDialog,
@ -75,36 +81,76 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
@state() private _systemStatusData?: SystemStatusStreamMessage; @state() private _systemStatusData?: SystemStatusStreamMessage;
@state() private _configEntries?: { [id: string]: ConfigEntry };
private _memoryEntries: { x: number; y: number | null }[] = []; private _memoryEntries: { x: number; y: number | null }[] = [];
private _cpuEntries: { x: number; y: number | null }[] = []; private _cpuEntries: { x: number; y: number | null }[] = [];
public hassSubscribe() { public hassSubscribe(): Array<UnsubscribeFunc | Promise<UnsubscribeFunc>> {
return isComponentLoaded(this.hass, "hardware") const subs = [
? [ subscribeConfigEntries(
this.hass.connection.subscribeMessage<SystemStatusStreamMessage>( this.hass,
(message) => { (messages) => {
// Only store the last 60 entries let fullUpdate = false;
this._memoryEntries.shift(); const newEntries: ConfigEntry[] = [];
this._cpuEntries.shift(); messages.forEach((message) => {
if (message.type === null || message.type === "added") {
this._memoryEntries.push({ newEntries.push(message.entry);
x: new Date(message.timestamp).getTime(), if (message.type === null) {
y: message.memory_used_percent, fullUpdate = true;
}); }
this._cpuEntries.push({ } else if (message.type === "removed") {
x: new Date(message.timestamp).getTime(), delete this._configEntries![message.entry.entry_id];
y: message.cpu_percent, } else if (message.type === "updated") {
}); const newEntry = message.entry;
this._configEntries![message.entry.entry_id] = newEntry;
this._systemStatusData = message;
},
{
type: "hardware/subscribe_system_status",
} }
), });
] if (!newEntries.length && !fullUpdate) {
: []; return;
}
const entries = [
...(fullUpdate ? [] : Object.values(this._configEntries!)),
...newEntries,
];
const configEntries: { [id: string]: ConfigEntry } = {};
for (const entry of entries) {
configEntries[entry.entry_id] = entry;
}
this._configEntries = configEntries;
},
{ type: ["hardware"] }
),
];
if (isComponentLoaded(this.hass, "hardware")) {
subs.push(
this.hass.connection.subscribeMessage<SystemStatusStreamMessage>(
(message) => {
// Only store the last 60 entries
this._memoryEntries.shift();
this._cpuEntries.shift();
this._memoryEntries.push({
x: new Date(message.timestamp).getTime(),
y: message.memory_used_percent,
});
this._cpuEntries.push({
x: new Date(message.timestamp).getTime(),
y: message.cpu_percent,
});
this._systemStatusData = message;
},
{
type: "hardware/subscribe_system_status",
}
)
);
}
return subs;
} }
protected willUpdate(): void { protected willUpdate(): void {
@ -175,19 +221,27 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
let boardName: string | undefined; let boardName: string | undefined;
let imageURL: string | undefined; let imageURL: string | undefined;
let documentationURL: string | undefined; let documentationURL: string | undefined;
let boardConfigEntries: ConfigEntry[] = [];
const boardData = this._hardwareInfo?.hardware.find( const boardData = this._hardwareInfo?.hardware.find(
(hw) => hw.board !== null (hw) => hw.board !== null
); );
const dongles = this._hardwareInfo?.hardware.filter(
(hw) => hw.dongle !== null
);
if (boardData) { if (boardData) {
boardId = boardData.board.hassio_board_id; boardConfigEntries = boardData.config_entries
.map((id) => this._configEntries?.[id])
.filter((entry) => entry?.supports_options) as ConfigEntry[];
boardId = boardData.board!.hassio_board_id;
boardName = boardData.name; boardName = boardData.name;
documentationURL = boardData.url; documentationURL = boardData.url;
imageURL = hardwareBrandsUrl({ imageURL = hardwareBrandsUrl({
category: "boards", category: "boards",
manufacturer: boardData.board.manufacturer, manufacturer: boardData.board!.manufacturer,
model: boardData.board.model, model: boardData.board!.model,
darkOptimized: this.hass.themes?.darkMode, darkOptimized: this.hass.themes?.darkMode,
}); });
} else if (this._OSData?.board) { } else if (this._OSData?.board) {
@ -243,7 +297,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
<ha-card outlined> <ha-card outlined>
<div class="card-content"> <div class="card-content">
<mwc-list> <mwc-list>
<mwc-list-item <ha-list-item
noninteractive noninteractive
graphic=${ifDefined(imageURL ? "medium" : undefined)} graphic=${ifDefined(imageURL ? "medium" : undefined)}
.twoline=${Boolean(boardId)} .twoline=${Boolean(boardId)}
@ -262,7 +316,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
> >
` `
: ""} : ""}
</mwc-list-item> </ha-list-item>
${documentationURL ${documentationURL
? html` ? html`
<ha-clickable-list-item <ha-clickable-list-item
@ -287,9 +341,42 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
: ""} : ""}
</mwc-list> </mwc-list>
</div> </div>
${boardConfigEntries.length
? html`<div class="card-actions">
<mwc-button
.entry=${boardConfigEntries[0]}
@click=${this._openOptionsFlow}
>
${this.hass.localize(
"ui.panel.config.hardware.configure"
)}
</mwc-button>
</div>`
: ""}
</ha-card> </ha-card>
` `
: ""} : ""}
${dongles?.length
? html`<ha-card>
${dongles.map((dongle) => {
const configEntry = dongle.config_entries
.map((id) => this._configEntries?.[id])
.filter((entry) => entry?.supports_options)[0];
return html`<div class="row">
${dongle.name}${configEntry
? html`<mwc-button
.entry=${configEntry}
@click=${this._openOptionsFlow}
>
${this.hass.localize(
"ui.panel.config.hardware.configure"
)}
</mwc-button>`
: ""}
</div>`;
})}
</ha-card>`
: ""}
${this._systemStatusData ${this._systemStatusData
? html`<ha-card outlined> ? html`<ha-card outlined>
<div class="header"> <div class="header">
@ -372,6 +459,14 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
} }
} }
private async _openOptionsFlow(ev) {
const entry = ev.currentTarget.entry;
if (!entry) {
return;
}
showOptionsFlowDialog(this, entry);
}
private async _openHardware() { private async _openHardware() {
showhardwareAvailableDialog(this); showhardwareAvailableDialog(this);
} }
@ -493,6 +588,13 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
.header .value { .header .value {
font-size: 16px; font-size: 16px;
} }
.row {
display: flex;
justify-content: space-between;
align-items: center;
height: 48px;
padding: 8px 16px;
}
`, `,
]; ];
} }

View File

@ -1710,6 +1710,7 @@
"failed_to_shutdown_host": "Failed to shutdown system", "failed_to_shutdown_host": "Failed to shutdown system",
"board": "Board", "board": "Board",
"documentation": "Documentation", "documentation": "Documentation",
"configure": "Configure",
"documentation_description": "Find extra information about your device" "documentation_description": "Find extra information about your device"
}, },
"info": { "info": {