diff --git a/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts b/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts new file mode 100755 index 0000000000..f7ccbd970b --- /dev/null +++ b/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts @@ -0,0 +1,194 @@ +import { mdiClose } from "@mdi/js"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../src/common/dom/fire_event"; +import "../../../../src/common/search/search-input"; +import { compare } from "../../../../src/common/string/compare"; +import "../../../../src/components/ha-dialog"; +import "../../../../src/components/ha-expansion-panel"; +import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware"; +import { dump } from "../../../../src/resources/js-yaml-dump"; +import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; +import { HomeAssistant } from "../../../../src/types"; +import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware"; + +const _filterDevices = memoizeOne( + (showAdvanced: boolean, hardware: HassioHardwareInfo, filter: string) => + hardware.devices + .filter( + (device) => + (showAdvanced || + ["tty", "gpio", "input"].includes(device.subsystem)) && + (device.by_id?.toLowerCase().includes(filter) || + device.name.toLowerCase().includes(filter) || + device.dev_path.toLocaleLowerCase().includes(filter) || + JSON.stringify(device.attributes) + .toLocaleLowerCase() + .includes(filter)) + ) + .sort((a, b) => compare(a.name, b.name)) +); + +@customElement("dialog-hassio-hardware") +class HassioHardwareDialog extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _dialogParams?: HassioHardwareDialogParams; + + @state() private _filter?: string; + + public showDialog(params: HassioHardwareDialogParams) { + this._dialogParams = params; + } + + public closeDialog() { + this._dialogParams = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render(): TemplateResult { + if (!this._dialogParams) { + return html``; + } + + const devices = _filterDevices( + this.hass.userData?.showAdvanced || false, + this._dialogParams.hardware, + (this._filter || "").toLowerCase() + ); + + return html` + +
+

+ ${this._dialogParams.supervisor.localize("dialog.hardware.title")} +

+ + + + + +
+ + ${devices.map( + (device) => + html` +
+ + ${this._dialogParams!.supervisor.localize( + "dialog.hardware.subsystem" + )}: + + ${device.subsystem} +
+
+ + ${this._dialogParams!.supervisor.localize( + "dialog.hardware.device_path" + )}: + + ${device.dev_path} +
+ ${device.by_id + ? html`
+ + ${this._dialogParams!.supervisor.localize( + "dialog.hardware.id" + )}: + + ${device.by_id} +
` + : ""} +
+ + ${this._dialogParams!.supervisor.localize( + "dialog.hardware.attributes" + )}: + +
${dump(device.attributes, { indent: 2 })}
+
+
` + )} +
+ `; + } + + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + haStyleDialog, + css` + mwc-icon-button { + position: absolute; + right: 16px; + top: 10px; + text-decoration: none; + color: var(--primary-text-color); + } + h2 { + margin: 18px 42px 0 18px; + color: var(--primary-text-color); + } + + ha-expansion-panel { + margin: 4px 0; + } + pre, + code { + background-color: var(--markdown-code-background-color, none); + border-radius: 3px; + } + pre { + padding: 16px; + overflow: auto; + line-height: 1.45; + font-family: var(--code-font-family, monospace); + } + code { + font-size: 85%; + padding: 0.2em 0.4em; + } + search-input { + margin: 0 16px; + display: block; + } + .device-property { + display: flex; + justify-content: space-between; + } + .attributes { + margin-top: 12px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-hassio-hardware": HassioHardwareDialog; + } +} diff --git a/hassio/src/dialogs/hardware/show-dialog-hassio-hardware.ts b/hassio/src/dialogs/hardware/show-dialog-hassio-hardware.ts new file mode 100644 index 0000000000..4ca15734e8 --- /dev/null +++ b/hassio/src/dialogs/hardware/show-dialog-hassio-hardware.ts @@ -0,0 +1,19 @@ +import { fireEvent } from "../../../../src/common/dom/fire_event"; +import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware"; +import { Supervisor } from "../../../../src/data/supervisor/supervisor"; + +export interface HassioHardwareDialogParams { + supervisor: Supervisor; + hardware: HassioHardwareInfo; +} + +export const showHassioHardwareDialog = ( + element: HTMLElement, + dialogParams: HassioHardwareDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-hassio-hardware", + dialogImport: () => import("./dialog-hassio-hardware"), + dialogParams, + }); +}; diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index cde6becf58..1381bf291a 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -2,7 +2,6 @@ import "@material/mwc-button"; import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; import "@material/mwc-list/mwc-list-item"; import { mdiDotsVertical } from "@mdi/js"; -import { dump } from "js-yaml"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -41,8 +40,8 @@ import { roundWithOneDecimal, } from "../../../src/util/calculate"; import "../components/supervisor-metric"; -import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown"; import { showNetworkDialog } from "../dialogs/network/show-dialog-network"; +import { showHassioHardwareDialog } from "../dialogs/hardware/show-dialog-hassio-hardware"; import { hassioStyle } from "../resources/hassio-style"; @customElement("hassio-host-info") @@ -229,20 +228,19 @@ class HassioHostInfo extends LitElement { } private async _showHardware(): Promise { + let hardware; try { - const content = await fetchHassioHardwareInfo(this.hass); - showHassioMarkdownDialog(this, { - title: this.supervisor.localize("system.host.hardware"), - content: `
${dump(content, { indent: 2 })}
`, - }); + hardware = await fetchHassioHardwareInfo(this.hass); } catch (err) { - showAlertDialog(this, { + await showAlertDialog(this, { title: this.supervisor.localize( "system.host.failed_to_get_hardware_list" ), text: extractApiErrorMessage(err), }); + return; } + showHassioHardwareDialog(this, { supervisor: this.supervisor, hardware }); } private async _hostReboot(ev: CustomEvent): Promise { diff --git a/src/components/ha-expansion-panel.ts b/src/components/ha-expansion-panel.ts index f553e3ffc6..705b5a6b81 100644 --- a/src/components/ha-expansion-panel.ts +++ b/src/components/ha-expansion-panel.ts @@ -14,12 +14,17 @@ class HaExpansionPanel extends LitElement { @property() header?: string; + @property() secondary?: string; + @query(".container") private _container!: HTMLDivElement; protected render(): TemplateResult { return html`
- ${this.header} + + ${this.header} + ${this.secondary} + ; + by_id: null | string; + dev_path: string; + name: string; + subsystem: string; + sysfs: string; +} + export interface HassioHardwareInfo { - serial: string[]; - input: string[]; - disk: string[]; - gpio: string[]; - audio: Record; + devices: HardwareDevice[]; } export const fetchHassioHardwareAudio = async ( diff --git a/src/translations/en.json b/src/translations/en.json index 22cb216393..a3b898bd77 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3980,6 +3980,14 @@ "create_snapshot": "Create a snapshot of {name} before updating", "updating": "Updating {name} to version {version}", "snapshotting": "Creating snapshot of {name}" + }, + "hardware": { + "title": "Hardware", + "search": "Search hardware", + "subsystem": "Subsystem", + "id": "ID", + "attributes": "Attributes", + "device_path": "Device path" } } }