From c73677f15d56b6f3f26a0682528cf5a93626294e Mon Sep 17 00:00:00 2001 From: Zack Barett Date: Wed, 27 Jul 2022 04:23:55 -0500 Subject: [PATCH] Move System Information to Repairs (#13281) Co-authored-by: Bram Kragten --- hassio/src/system/hassio-supervisor-info.ts | 2 +- src/panels/config/ha-panel-config.ts | 12 - .../repairs/dialog-integration-startup.ts | 57 ++ .../dialog-system-information.ts} | 521 +++++++++--------- .../repairs/ha-config-repairs-dashboard.ts | 63 ++- .../integrations-startup-time.ts} | 96 ++-- .../show-integration-startup-dialog.ts | 12 + .../repairs/show-system-information-dialog.ts | 12 + src/translations/en.json | 3 + 9 files changed, 431 insertions(+), 347 deletions(-) create mode 100644 src/panels/config/repairs/dialog-integration-startup.ts rename src/panels/config/{system-health/ha-config-system-health.ts => repairs/dialog-system-information.ts} (61%) rename src/panels/config/{system-health/integrations-card.ts => repairs/integrations-startup-time.ts} (55%) create mode 100644 src/panels/config/repairs/show-integration-startup-dialog.ts create mode 100644 src/panels/config/repairs/show-system-information-dialog.ts diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts index 2623cdcf25..e1acfc74f1 100644 --- a/hassio/src/system/hassio-supervisor-info.ts +++ b/hassio/src/system/hassio-supervisor-info.ts @@ -26,7 +26,7 @@ import { import { UNHEALTHY_REASON_URL, UNSUPPORTED_REASON_URL, -} from "../../../src/panels/config/system-health/ha-config-system-health"; +} from "../../../src/panels/config/repairs/dialog-system-information"; import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant } from "../../../src/types"; import { bytesToString } from "../../../src/util/bytes-to-string"; diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index 67cbdcfa62..c910dfdbeb 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -6,7 +6,6 @@ import { mdiCog, mdiDatabase, mdiDevices, - mdiHeart, mdiInformation, mdiInformationOutline, mdiLifebuoy, @@ -322,13 +321,6 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconColor: "#301A8E", component: "hassio", }, - { - path: "/config/system_health", - translationKey: "system_health", - iconPath: mdiHeart, - iconColor: "#507FfE", - components: ["system_health", "hassio"], - }, ], about: [ { @@ -447,10 +439,6 @@ class HaPanelConfig extends HassRouterPage { tag: "ha-config-section-storage", load: () => import("./storage/ha-config-section-storage"), }, - system_health: { - tag: "ha-config-system-health", - load: () => import("./system-health/ha-config-system-health"), - }, updates: { tag: "ha-config-section-updates", load: () => import("./core/ha-config-section-updates"), diff --git a/src/panels/config/repairs/dialog-integration-startup.ts b/src/panels/config/repairs/dialog-integration-startup.ts new file mode 100644 index 0000000000..971be731f6 --- /dev/null +++ b/src/panels/config/repairs/dialog-integration-startup.ts @@ -0,0 +1,57 @@ +import "@material/mwc-button/mwc-button"; +import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../common/dom/fire_event"; +import "../../../components/ha-card"; +import { createCloseHeading } from "../../../components/ha-dialog"; +import { haStyleDialog } from "../../../resources/styles"; +import type { HomeAssistant } from "../../../types"; +import "./integrations-startup-time"; + +@customElement("dialog-integration-startup") +class DialogIntegrationStartup extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _opened = false; + + public showDialog(): void { + this._opened = true; + } + + public closeDialog() { + this._opened = false; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render(): TemplateResult { + if (!this._opened) { + return html``; + } + + return html` + + + + `; + } + + static styles: CSSResultGroup = haStyleDialog; +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-integration-startup": DialogIntegrationStartup; + } +} diff --git a/src/panels/config/system-health/ha-config-system-health.ts b/src/panels/config/repairs/dialog-system-information.ts similarity index 61% rename from src/panels/config/system-health/ha-config-system-health.ts rename to src/panels/config/repairs/dialog-system-information.ts index ba3c2862b0..d6e6851734 100644 --- a/src/panels/config/system-health/ha-config-system-health.ts +++ b/src/panels/config/repairs/dialog-system-information.ts @@ -1,17 +1,15 @@ -import { ActionDetail } from "@material/mwc-list"; -import "@material/mwc-list/mwc-list-item"; -import { mdiContentCopy } from "@mdi/js"; -import { UnsubscribeFunc } from "home-assistant-js-websocket/dist/types"; +import "@material/mwc-button/mwc-button"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { formatDateTime } from "../../../common/datetime/format_date_time"; +import { fireEvent } from "../../../common/dom/fire_event"; import { copyToClipboard } from "../../../common/util/copy-clipboard"; import { subscribePollingCollection } from "../../../common/util/subscribe-polling"; import "../../../components/ha-alert"; -import "../../../components/ha-button-menu"; import "../../../components/ha-card"; -import "../../../components/ha-circular-progress"; +import { createCloseHeading } from "../../../components/ha-dialog"; import "../../../components/ha-metric"; import { fetchHassioStats, HassioStats } from "../../../data/hassio/common"; import { @@ -25,12 +23,11 @@ import { SystemHealthInfo, } from "../../../data/system_health"; import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; -import "../../../layouts/hass-subpage"; -import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; +import { haStyleDialog } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { showToast } from "../../../util/toast"; -import "./integrations-card"; +import "../../../components/ha-circular-progress"; const sortKeys = (a: string, b: string) => { if (a === "homeassistant") { @@ -53,28 +50,40 @@ export const UNHEALTHY_REASON_URL = { privileged: "/more-info/unsupported/privileged", }; -@customElement("ha-config-system-health") -class HaConfigSystemHealth extends SubscribeMixin(LitElement) { +@customElement("dialog-system-information") +class DialogSystemInformation extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ type: Boolean }) public narrow!: boolean; - - @state() private _info?: SystemHealthInfo; - - @state() private _supervisorStats?: HassioStats; + @state() private _systemInfo?: SystemHealthInfo; @state() private _resolutionInfo?: HassioResolution; + @state() private _supervisorStats?: HassioStats; + @state() private _coreStats?: HassioStats; - @state() private _error?: { code: string; message: string }; + @state() private _opened = false; - public hassSubscribe(): Array> { + private _subscriptions?: Array>; + + public showDialog(): void { + this._opened = true; + this.hass!.loadBackendTranslation("system_health"); + this._subscribe(); + } + + public closeDialog() { + this._opened = false; + this._unsubscribe(); + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + private _subscribe(): void { const subs: Array> = []; if (isComponentLoaded(this.hass, "system_health")) { subs.push( subscribeSystemHealthInfo(this.hass!, (info) => { - this._info = info; + this._systemInfo = info; }) ); } @@ -93,149 +102,51 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) { 10000 ) ); + fetchHassioResolution(this.hass).then((data) => { this._resolutionInfo = data; }); } - return subs; + this._subscriptions = subs; } - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); + private _unsubscribe() { + while (this._subscriptions?.length) { + const unsub = this._subscriptions.pop()!; + if (unsub instanceof Promise) { + unsub.then((unsubFunc) => unsubFunc()); + } else { + unsub(); + } + } + this._subscriptions = undefined; - this.hass!.loadBackendTranslation("system_health"); + this._systemInfo = undefined; + this._resolutionInfo = undefined; + this._coreStats = undefined; + this._supervisorStats = undefined; } protected render(): TemplateResult { - const sections: TemplateResult[] = []; - - if (!this._info) { - sections.push( - html` -
- -
- ` - ); - } else { - const domains = Object.keys(this._info).sort(sortKeys); - for (const domain of domains) { - const domainInfo = this._info[domain]; - const keys: TemplateResult[] = []; - - for (const key of Object.keys(domainInfo.info)) { - let value: unknown; - - if ( - domainInfo.info[key] && - typeof domainInfo.info[key] === "object" - ) { - const info = domainInfo.info[key] as SystemCheckValueObject; - - if (info.type === "pending") { - value = html` - - `; - } else if (info.type === "failed") { - value = html` - ${info.error}${!info.more_info - ? "" - : html` - – - - ${this.hass.localize( - "ui.panel.config.info.system_health.more_info" - )} - - `} - `; - } else if (info.type === "date") { - value = formatDateTime(new Date(info.value), this.hass.locale); - } - } else { - value = domainInfo.info[key]; - } - - keys.push(html` - - - ${this.hass.localize( - `component.${domain}.system_health.info.${key}` - ) || key} - - ${value} - - `); - } - if (domain !== "homeassistant") { - sections.push( - html` -
-

${domainToName(this.hass.localize, domain)}

- ${!domainInfo.manage_url - ? "" - : html` - - - ${this.hass.localize( - "ui.panel.config.info.system_health.manage" - )} - - - `} -
- ` - ); - } - sections.push(html` - - ${keys} -
- `); - } + if (!this._opened) { + return html``; } + const sections = this._getSections(); + return html` - - ${this._error - ? html` - ${this._error.message || this._error.code} - ` - : ""} - ${this._info - ? html` - - - - ${this.hass.localize("ui.panel.config.info.copy_raw")} - - - ${this.hass.localize("ui.panel.config.info.copy_github")} - - - ` - : ""} -
+
${this._resolutionInfo ? html`${this._resolutionInfo.unhealthy.length ? html` @@ -265,66 +176,63 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) { : ""} ` : ""} - -
${sections}
-
+
${sections}
+ ${!this._coreStats && !this._supervisorStats ? "" : html` - -
- ${this._coreStats - ? html` -

- ${this.hass.localize( - "ui.panel.config.system_health.core_stats" - )} -

- - - ` - : ""} - ${this._supervisorStats - ? html` -

- ${this.hass.localize( - "ui.panel.config.system_health.supervisor_stats" - )} -

- - - ` - : ""} -
-
+
+ ${this._coreStats + ? html` +

+ ${this.hass.localize( + "ui.panel.config.system_health.core_stats" + )} +

+ + + ` + : ""} + ${this._supervisorStats + ? html` +

+ ${this.hass.localize( + "ui.panel.config.system_health.supervisor_stats" + )} +

+ + + ` + : ""} +
`} - -
- + + `; } @@ -386,17 +294,111 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) { }); } - private async _copyInfo(ev: CustomEvent): Promise { - const github = ev.detail.index === 1; + private _getSections(): TemplateResult[] { + const sections: TemplateResult[] = []; + + if (!this._systemInfo) { + sections.push( + html` +
+ +
+ ` + ); + } else { + const domains = Object.keys(this._systemInfo).sort(sortKeys); + for (const domain of domains) { + const domainInfo = this._systemInfo[domain]; + const keys: TemplateResult[] = []; + + for (const key of Object.keys(domainInfo.info)) { + let value: unknown; + + if ( + domainInfo.info[key] && + typeof domainInfo.info[key] === "object" + ) { + const info = domainInfo.info[key] as SystemCheckValueObject; + + if (info.type === "pending") { + value = html` + + `; + } else if (info.type === "failed") { + value = html` + ${info.error}${!info.more_info + ? "" + : html` + – + + ${this.hass.localize( + "ui.panel.config.info.system_health.more_systemInfo" + )} + + `} + `; + } else if (info.type === "date") { + value = formatDateTime(new Date(info.value), this.hass.locale); + } + } else { + value = domainInfo.info[key]; + } + + keys.push(html` + + + ${this.hass.localize( + `component.${domain}.system_health.info.${key}` + ) || key} + + ${value} + + `); + } + if (domain !== "homeassistant") { + sections.push( + html` +
+

${domainToName(this.hass.localize, domain)}

+ ${!domainInfo.manage_url + ? "" + : html` + + + ${this.hass.localize( + "ui.panel.config.info.system_health.manage" + )} + + + `} +
+ ` + ); + } + sections.push(html` + + ${keys} +
+ `); + } + } + return sections; + } + + private async _copyInfo(): Promise { let haContent: string | undefined; const domainParts: string[] = []; - for (const domain of Object.keys(this._info!).sort(sortKeys)) { - const domainInfo = this._info![domain]; + for (const domain of Object.keys(this._systemInfo!).sort(sortKeys)) { + const domainInfo = this._systemInfo![domain]; let first = true; const parts = [ `${ - github && domain !== "homeassistant" + domain !== "homeassistant" ? `
${domainToName( this.hass.localize, domain @@ -408,7 +410,7 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) { for (const key of Object.keys(domainInfo.info)) { let value: unknown; - if (typeof domainInfo.info[key] === "object") { + if (domainInfo.info[key] && typeof domainInfo.info[key] === "object") { const info = domainInfo.info[key] as SystemCheckValueObject; if (info.type === "pending") { @@ -421,11 +423,11 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) { } else { value = domainInfo.info[key]; } - if (github && first) { + if (first) { parts.push(`${key} | ${value}\n-- | --`); first = false; } else { - parts.push(`${key}${github ? " | " : ": "}${value}`); + parts.push(`${key} | ${value}`); } } @@ -433,16 +435,14 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) { haContent = parts.join("\n"); } else { domainParts.push(parts.join("\n")); - if (github && domain !== "homeassistant") { + if (domain !== "homeassistant") { domainParts.push("
"); } } } await copyToClipboard( - `${github ? "## " : ""}System Health\n${haContent}\n\n${domainParts.join( - "\n\n" - )}` + `${"## "}System Information\n${haContent}\n\n${domainParts.join("\n\n")}` ); showToast(this, { @@ -450,73 +450,50 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) { }); } - static styles: CSSResultGroup = css` - .content { - padding: 28px 20px 0; - max-width: 1040px; - margin: 0 auto; - } - integrations-card { - max-width: 600px; - display: block; - max-width: 600px; - margin: 0 auto; - margin-bottom: 24px; - margin-bottom: max(24px, env(safe-area-inset-bottom)); - } - ha-card { - display: block; - max-width: 600px; - margin: 0 auto; - padding-bottom: 16px; - margin-bottom: 24px; - } - ha-alert { - display: block; - max-width: 500px; - margin: 0 auto; - margin-bottom: max(24px, env(safe-area-inset-bottom)); - } - table { - width: 100%; - } + static styles: CSSResultGroup = [ + haStyleDialog, + css` + ha-alert { + margin-bottom: 16px; + display: block; + } + table { + width: 100%; + } - td:first-child { - width: 45%; - } + td:first-child { + width: 45%; + } - td:last-child { - direction: ltr; - } + td:last-child { + direction: ltr; + } - .loading-container { - display: flex; - align-items: center; - justify-content: center; - } + .loading-container { + display: flex; + align-items: center; + justify-content: center; + } - .card-header { - justify-content: space-between; - display: flex; - align-items: center; - } + .card-header { + justify-content: space-between; + display: flex; + align-items: center; + } - .error { - color: var(--error-color); - } + .error { + color: var(--error-color); + } - a { - color: var(--primary-color); - } - - a.manage { - text-decoration: none; - } - `; + a.manage { + text-decoration: none; + } + `, + ]; } declare global { interface HTMLElementTagNameMap { - "ha-config-system-health": HaConfigSystemHealth; + "dialog-system-information": DialogSystemInformation; } } diff --git a/src/panels/config/repairs/ha-config-repairs-dashboard.ts b/src/panels/config/repairs/ha-config-repairs-dashboard.ts index 21f46e0db0..7b1968b572 100644 --- a/src/panels/config/repairs/ha-config-repairs-dashboard.ts +++ b/src/panels/config/repairs/ha-config-repairs-dashboard.ts @@ -1,8 +1,10 @@ -import type { ActionDetail } from "@material/mwc-list"; +import { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item-base"; import { mdiDotsVertical } from "@mdi/js"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; +import { isComponentLoaded } from "../../../common/config/is_component_loaded"; +import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event"; import "../../../components/ha-card"; import { fetchRepairsIssues, @@ -10,11 +12,14 @@ import { severitySort, } from "../../../data/repairs"; import "../../../layouts/hass-subpage"; +import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import type { HomeAssistant } from "../../../types"; import "./ha-config-repairs"; +import { showIntegrationStartupDialog } from "./show-integration-startup-dialog"; +import { showSystemInformationDialog } from "./show-system-information-dialog"; @customElement("ha-config-repairs-dashboard") -class HaConfigRepairsDashboard extends LitElement { +class HaConfigRepairsDashboard extends SubscribeMixin(LitElement) { @property({ attribute: false }) public hass!: HomeAssistant; @property({ type: Boolean }) public narrow!: boolean; @@ -40,6 +45,7 @@ class HaConfigRepairsDashboard extends LitElement { this._showIgnored, this._repairsIssues ); + return html`
- + - + ${isComponentLoaded(this.hass, "system_health") || + isComponentLoaded(this.hass, "hassio") + ? html` + + ${this.hass.localize( + "ui.panel.config.repairs.system_information" + )} + + ` + : ""} + + ${this.hass.localize( + "ui.panel.config.repairs.integration_startup_time" + )} + + ${this._showIgnored ? this.hass.localize("ui.panel.config.repairs.hide_ignored") : this.hass.localize("ui.panel.config.repairs.show_ignored")} @@ -98,12 +123,32 @@ class HaConfigRepairsDashboard extends LitElement { this.hass.loadBackendTranslation("issues", [...integrations]); } - private _handleAction(ev: CustomEvent) { - switch (ev.detail.index) { - case 0: - this._showIgnored = !this._showIgnored; - break; + private _showSystemInformationDialog( + ev: CustomEvent + ): void { + if (!shouldHandleRequestSelectedEvent(ev)) { + return; } + + showSystemInformationDialog(this); + } + + private _showIntegrationStartupDialog( + ev: CustomEvent + ): void { + if (!shouldHandleRequestSelectedEvent(ev)) { + return; + } + + showIntegrationStartupDialog(this); + } + + private _toggleIgnored(ev: CustomEvent): void { + if (!shouldHandleRequestSelectedEvent(ev)) { + return; + } + + this._showIgnored = !this._showIgnored; } static styles = css` diff --git a/src/panels/config/system-health/integrations-card.ts b/src/panels/config/repairs/integrations-startup-time.ts similarity index 55% rename from src/panels/config/system-health/integrations-card.ts rename to src/panels/config/repairs/integrations-startup-time.ts index 2188160eb7..fb73329905 100644 --- a/src/panels/config/system-health/integrations-card.ts +++ b/src/panels/config/repairs/integrations-startup-time.ts @@ -21,8 +21,8 @@ import type { HomeAssistant } from "../../../types"; import { brandsUrl } from "../../../util/brands-url"; import { documentationUrl } from "../../../util/documentation-url"; -@customElement("integrations-card") -class IntegrationsCard extends LitElement { +@customElement("integrations-startup-time") +class IntegrationsStartupTime extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property({ type: Boolean }) public narrow = false; @@ -45,57 +45,47 @@ class IntegrationsCard extends LitElement { } return html` - - - ${this._setups?.map((setup) => { - const manifest = this._manifests && this._manifests[setup.domain]; - const docLink = manifest - ? manifest.is_built_in - ? documentationUrl( - this.hass, - `/integrations/${manifest.domain}` - ) - : manifest.documentation - : ""; + + ${this._setups?.map((setup) => { + const manifest = this._manifests && this._manifests[setup.domain]; + const docLink = manifest + ? manifest.is_built_in + ? documentationUrl(this.hass, `/integrations/${manifest.domain}`) + : manifest.documentation + : ""; - const setupSeconds = setup.seconds?.toFixed(2); - return html` - - - - ${domainToName(this.hass.localize, setup.domain, manifest)} - - ${setup.domain} -
- ${setupSeconds ? html`${setupSeconds} s` : ""} -
-
- `; - })} -
-
+ const setupSeconds = setup.seconds?.toFixed(2); + return html` + + + + ${domainToName(this.hass.localize, setup.domain, manifest)} + + ${setup.domain} +
+ ${setupSeconds ? html`${setupSeconds} s` : ""} +
+
+ `; + })} + `; } @@ -149,6 +139,6 @@ class IntegrationsCard extends LitElement { declare global { interface HTMLElementTagNameMap { - "integrations-card": IntegrationsCard; + "integrations-startup-time": IntegrationsStartupTime; } } diff --git a/src/panels/config/repairs/show-integration-startup-dialog.ts b/src/panels/config/repairs/show-integration-startup-dialog.ts new file mode 100644 index 0000000000..5eb9124645 --- /dev/null +++ b/src/panels/config/repairs/show-integration-startup-dialog.ts @@ -0,0 +1,12 @@ +import { fireEvent } from "../../../common/dom/fire_event"; + +export const loadIntegrationStartupDialog = () => + import("./dialog-integration-startup"); + +export const showIntegrationStartupDialog = (element: HTMLElement): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-integration-startup", + dialogImport: loadIntegrationStartupDialog, + dialogParams: {}, + }); +}; diff --git a/src/panels/config/repairs/show-system-information-dialog.ts b/src/panels/config/repairs/show-system-information-dialog.ts new file mode 100644 index 0000000000..1cbf5917f3 --- /dev/null +++ b/src/panels/config/repairs/show-system-information-dialog.ts @@ -0,0 +1,12 @@ +import { fireEvent } from "../../../common/dom/fire_event"; + +export const loadSystemInformationDialog = () => + import("./dialog-system-information"); + +export const showSystemInformationDialog = (element: HTMLElement): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-system-information", + dialogImport: loadSystemInformationDialog, + dialogParams: undefined, + }); +}; diff --git a/src/translations/en.json b/src/translations/en.json index 1bcdd0cd4f..117feb5073 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1237,6 +1237,9 @@ "critical": "Critical", "error": "Error", "warning": "Warning", + "system_information": "System information", + "integration_startup_time": "Integration startup time", + "copy": "Copy", "dialog": { "title": "Repair", "fix": "Repair",