From fce87ff0fe7c2f8f2e335bf96515cb67630cc705 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 28 Nov 2022 12:47:27 +0100 Subject: [PATCH] Add support for hardware option flows (#14364) --- src/data/hardware.ts | 12 +- src/data/integration.ts | 7 +- .../config/hardware/ha-config-hardware.ts | 162 ++++++++++++++---- src/translations/en.json | 1 + 4 files changed, 150 insertions(+), 32 deletions(-) diff --git a/src/data/hardware.ts b/src/data/hardware.ts index f612512161..accbf4d9ba 100644 --- a/src/data/hardware.ts +++ b/src/data/hardware.ts @@ -26,7 +26,9 @@ export interface HardwareInfo { } export interface HardwareInfoEntry { - board: HardwareInfoBoardInfo; + board: HardwareInfoBoardInfo | null; + dongle: HardwareInfoDongleInfo | null; + config_entries: string[]; name: string; url?: string; } @@ -38,6 +40,14 @@ export interface HardwareInfoBoardInfo { hassio_board_id?: string; } +export interface HardwareInfoDongleInfo { + manufacturer: string; + description: string; + pid?: string; + vid?: string; + serial_number?: string; +} + export interface SystemStatusStreamMessage { cpu_percent: number; memory_free_mb: number; diff --git a/src/data/integration.ts b/src/data/integration.ts index e644cd64be..81c28b5945 100644 --- a/src/data/integration.ts +++ b/src/data/integration.ts @@ -1,7 +1,12 @@ import { LocalizeFunc } from "../common/translations/localize"; import { HomeAssistant } from "../types"; -export type IntegrationType = "device" | "helper" | "hub" | "service"; +export type IntegrationType = + | "device" + | "helper" + | "hub" + | "service" + | "hardware"; export interface IntegrationManifest { is_built_in: boolean; diff --git a/src/panels/config/hardware/ha-config-hardware.ts b/src/panels/config/hardware/ha-config-hardware.ts index 83f664934d..df6ad666fd 100644 --- a/src/panels/config/hardware/ha-config-hardware.ts +++ b/src/panels/config/hardware/ha-config-hardware.ts @@ -2,6 +2,7 @@ import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list-item"; import { mdiDotsVertical } from "@mdi/js"; import type { ChartOptions } from "chart.js"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; @@ -16,6 +17,10 @@ import "../../../components/ha-card"; import "../../../components/ha-clickable-list-item"; import "../../../components/ha-icon-next"; import "../../../components/ha-settings-row"; +import { + ConfigEntry, + subscribeConfigEntries, +} from "../../../data/config_entries"; import { BOARD_NAMES, HardwareInfo, @@ -33,6 +38,7 @@ import { rebootHost, shutdownHost, } from "../../../data/hassio/host"; +import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow"; import { showAlertDialog, showConfirmationDialog, @@ -75,36 +81,76 @@ class HaConfigHardware extends SubscribeMixin(LitElement) { @state() private _systemStatusData?: SystemStatusStreamMessage; + @state() private _configEntries?: { [id: string]: ConfigEntry }; + private _memoryEntries: { x: number; y: number | null }[] = []; private _cpuEntries: { x: number; y: number | null }[] = []; - public hassSubscribe() { - return isComponentLoaded(this.hass, "hardware") - ? [ - this.hass.connection.subscribeMessage( - (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", + public hassSubscribe(): Array> { + const subs = [ + subscribeConfigEntries( + this.hass, + (messages) => { + let fullUpdate = false; + const newEntries: ConfigEntry[] = []; + messages.forEach((message) => { + if (message.type === null || message.type === "added") { + newEntries.push(message.entry); + if (message.type === null) { + fullUpdate = true; + } + } else if (message.type === "removed") { + delete this._configEntries![message.entry.entry_id]; + } else if (message.type === "updated") { + const newEntry = message.entry; + this._configEntries![message.entry.entry_id] = newEntry; } - ), - ] - : []; + }); + 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( + (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 { @@ -175,19 +221,27 @@ class HaConfigHardware extends SubscribeMixin(LitElement) { let boardName: string | undefined; let imageURL: string | undefined; let documentationURL: string | undefined; + let boardConfigEntries: ConfigEntry[] = []; const boardData = this._hardwareInfo?.hardware.find( (hw) => hw.board !== null ); + const dongles = this._hardwareInfo?.hardware.filter( + (hw) => hw.dongle !== null + ); + 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; documentationURL = boardData.url; imageURL = hardwareBrandsUrl({ category: "boards", - manufacturer: boardData.board.manufacturer, - model: boardData.board.model, + manufacturer: boardData.board!.manufacturer, + model: boardData.board!.model, darkOptimized: this.hass.themes?.darkMode, }); } else if (this._OSData?.board) { @@ -243,7 +297,7 @@ class HaConfigHardware extends SubscribeMixin(LitElement) {
- ` : ""} - + ${documentationURL ? html`
+ ${boardConfigEntries.length + ? html`
+ + ${this.hass.localize( + "ui.panel.config.hardware.configure" + )} + +
` + : ""}
` : ""} + ${dongles?.length + ? html` + ${dongles.map((dongle) => { + const configEntry = dongle.config_entries + .map((id) => this._configEntries?.[id]) + .filter((entry) => entry?.supports_options)[0]; + return html`
+ ${dongle.name}${configEntry + ? html` + ${this.hass.localize( + "ui.panel.config.hardware.configure" + )} + ` + : ""} +
`; + })} +
` + : ""} ${this._systemStatusData ? html`
@@ -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() { showhardwareAvailableDialog(this); } @@ -493,6 +588,13 @@ class HaConfigHardware extends SubscribeMixin(LitElement) { .header .value { font-size: 16px; } + .row { + display: flex; + justify-content: space-between; + align-items: center; + height: 48px; + padding: 8px 16px; + } `, ]; } diff --git a/src/translations/en.json b/src/translations/en.json index 28d2187012..3d2ecc14f5 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1710,6 +1710,7 @@ "failed_to_shutdown_host": "Failed to shutdown system", "board": "Board", "documentation": "Documentation", + "configure": "Configure", "documentation_description": "Find extra information about your device" }, "info": {