Move System Information to Repairs (#13281)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Zack Barett 2022-07-27 04:23:55 -05:00 committed by GitHub
parent f7090583ac
commit c73677f15d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 431 additions and 347 deletions

View File

@ -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";

View File

@ -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"),

View File

@ -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`
<ha-dialog
open
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.repairs.integration_startup_time")
)}
>
<integrations-startup-time
.hass=${this.hass}
narrow
></integrations-startup-time>
</ha-dialog>
`;
}
static styles: CSSResultGroup = haStyleDialog;
}
declare global {
interface HTMLElementTagNameMap {
"dialog-integration-startup": DialogIntegrationStartup;
}
}

View File

@ -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<UnsubscribeFunc | Promise<UnsubscribeFunc>> {
private _subscriptions?: Array<UnsubscribeFunc | Promise<UnsubscribeFunc>>;
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<UnsubscribeFunc | Promise<UnsubscribeFunc>> = [];
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`
<div class="loading-container">
<ha-circular-progress active></ha-circular-progress>
</div>
`
);
} 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`
<ha-circular-progress active size="tiny"></ha-circular-progress>
`;
} else if (info.type === "failed") {
value = html`
<span class="error">${info.error}</span>${!info.more_info
? ""
: html`
<a
href=${info.more_info}
target="_blank"
rel="noreferrer noopener"
>
${this.hass.localize(
"ui.panel.config.info.system_health.more_info"
)}
</a>
`}
`;
} else if (info.type === "date") {
value = formatDateTime(new Date(info.value), this.hass.locale);
}
} else {
value = domainInfo.info[key];
}
keys.push(html`
<tr>
<td>
${this.hass.localize(
`component.${domain}.system_health.info.${key}`
) || key}
</td>
<td>${value}</td>
</tr>
`);
}
if (domain !== "homeassistant") {
sections.push(
html`
<div class="card-header">
<h3>${domainToName(this.hass.localize, domain)}</h3>
${!domainInfo.manage_url
? ""
: html`
<a class="manage" href=${domainInfo.manage_url}>
<mwc-button>
${this.hass.localize(
"ui.panel.config.info.system_health.manage"
)}
</mwc-button>
</a>
`}
</div>
`
);
}
sections.push(html`
<table>
${keys}
</table>
`);
}
if (!this._opened) {
return html``;
}
const sections = this._getSections();
return html`
<hass-subpage
.hass=${this.hass}
.narrow=${this.narrow}
back-path="/config/system"
.header=${this.hass.localize("ui.panel.config.system_health.caption")}
<ha-dialog
open
@closed=${this.closeDialog}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.repairs.system_information")
)}
>
${this._error
? html`
<ha-alert alert-type="error"
>${this._error.message || this._error.code}</ha-alert
>
`
: ""}
${this._info
? html`
<ha-button-menu
corner="BOTTOM_START"
slot="toolbar-icon"
@action=${this._copyInfo}
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.panel.config.info.copy_menu")}
.path=${mdiContentCopy}
></ha-icon-button>
<mwc-list-item>
${this.hass.localize("ui.panel.config.info.copy_raw")}
</mwc-list-item>
<mwc-list-item>
${this.hass.localize("ui.panel.config.info.copy_github")}
</mwc-list-item>
</ha-button-menu>
`
: ""}
<div class="content">
<div>
${this._resolutionInfo
? html`${this._resolutionInfo.unhealthy.length
? html`<ha-alert alert-type="error">
@ -265,66 +176,63 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
: ""} `
: ""}
<ha-card outlined>
<div class="card-content">${sections}</div>
</ha-card>
<div>${sections}</div>
${!this._coreStats && !this._supervisorStats
? ""
: html`
<ha-card outlined>
<div class="card-content">
${this._coreStats
? html`
<h3>
${this.hass.localize(
"ui.panel.config.system_health.core_stats"
)}
</h3>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.cpu_usage"
)}
.value=${this._coreStats.cpu_percent}
></ha-metric>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.ram_usage"
)}
.value=${this._coreStats.memory_percent}
></ha-metric>
`
: ""}
${this._supervisorStats
? html`
<h3>
${this.hass.localize(
"ui.panel.config.system_health.supervisor_stats"
)}
</h3>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.cpu_usage"
)}
.value=${this._supervisorStats.cpu_percent}
></ha-metric>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.ram_usage"
)}
.value=${this._supervisorStats.memory_percent}
></ha-metric>
`
: ""}
</div>
</ha-card>
<div>
${this._coreStats
? html`
<h3>
${this.hass.localize(
"ui.panel.config.system_health.core_stats"
)}
</h3>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.cpu_usage"
)}
.value=${this._coreStats.cpu_percent}
></ha-metric>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.ram_usage"
)}
.value=${this._coreStats.memory_percent}
></ha-metric>
`
: ""}
${this._supervisorStats
? html`
<h3>
${this.hass.localize(
"ui.panel.config.system_health.supervisor_stats"
)}
</h3>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.cpu_usage"
)}
.value=${this._supervisorStats.cpu_percent}
></ha-metric>
<ha-metric
.heading=${this.hass.localize(
"ui.panel.config.system_health.ram_usage"
)}
.value=${this._supervisorStats.memory_percent}
></ha-metric>
`
: ""}
</div>
`}
<integrations-card
.hass=${this.hass}
.narrow=${this.narrow}
></integrations-card>
</div>
</hass-subpage>
<mwc-button
slot="primaryAction"
.label=${this.hass.localize("ui.panel.config.repairs.copy")}
@click=${this._copyInfo}
></mwc-button>
</ha-dialog>
`;
}
@ -386,17 +294,111 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
});
}
private async _copyInfo(ev: CustomEvent<ActionDetail>): Promise<void> {
const github = ev.detail.index === 1;
private _getSections(): TemplateResult[] {
const sections: TemplateResult[] = [];
if (!this._systemInfo) {
sections.push(
html`
<div class="loading-container">
<ha-circular-progress active></ha-circular-progress>
</div>
`
);
} 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`
<ha-circular-progress active size="tiny"></ha-circular-progress>
`;
} else if (info.type === "failed") {
value = html`
<span class="error">${info.error}</span>${!info.more_info
? ""
: html`
<a
href=${info.more_info}
target="_blank"
rel="noreferrer noopener"
>
${this.hass.localize(
"ui.panel.config.info.system_health.more_systemInfo"
)}
</a>
`}
`;
} else if (info.type === "date") {
value = formatDateTime(new Date(info.value), this.hass.locale);
}
} else {
value = domainInfo.info[key];
}
keys.push(html`
<tr>
<td>
${this.hass.localize(
`component.${domain}.system_health.info.${key}`
) || key}
</td>
<td>${value}</td>
</tr>
`);
}
if (domain !== "homeassistant") {
sections.push(
html`
<div class="card-header">
<h3>${domainToName(this.hass.localize, domain)}</h3>
${!domainInfo.manage_url
? ""
: html`
<a class="manage" href=${domainInfo.manage_url}>
<mwc-button>
${this.hass.localize(
"ui.panel.config.info.system_health.manage"
)}
</mwc-button>
</a>
`}
</div>
`
);
}
sections.push(html`
<table>
${keys}
</table>
`);
}
}
return sections;
}
private async _copyInfo(): Promise<void> {
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"
? `<details><summary>${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("</details>");
}
}
}
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;
}
}

View File

@ -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`
<hass-subpage
back-path="/config/system"
@ -48,13 +54,32 @@ class HaConfigRepairsDashboard extends LitElement {
.header=${this.hass.localize("ui.panel.config.repairs.caption")}
>
<div slot="toolbar-icon">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<ha-button-menu corner="BOTTOM_START">
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item id="skipped">
${isComponentLoaded(this.hass, "system_health") ||
isComponentLoaded(this.hass, "hassio")
? html`
<mwc-list-item
@request-selected=${this._showSystemInformationDialog}
>
${this.hass.localize(
"ui.panel.config.repairs.system_information"
)}
</mwc-list-item>
`
: ""}
<mwc-list-item
@request-selected=${this._showIntegrationStartupDialog}
>
${this.hass.localize(
"ui.panel.config.repairs.integration_startup_time"
)}
</mwc-list-item>
<mwc-list-item @request-selected=${this._toggleIgnored}>
${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<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._showIgnored = !this._showIgnored;
break;
private _showSystemInformationDialog(
ev: CustomEvent<RequestSelectedDetail>
): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
showSystemInformationDialog(this);
}
private _showIntegrationStartupDialog(
ev: CustomEvent<RequestSelectedDetail>
): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
showIntegrationStartupDialog(this);
}
private _toggleIgnored(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
this._showIgnored = !this._showIgnored;
}
static styles = css`

View File

@ -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`
<ha-card
outlined
.header=${this.hass.localize(
"ui.panel.config.system_health.integration_start_time"
)}
>
<mwc-list>
${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
: "";
<mwc-list>
${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`
<ha-clickable-list-item
graphic="avatar"
twoline
hasMeta
openNewTab
@click=${this._entryClicked}
href=${docLink}
>
<img
loading="lazy"
src=${brandsUrl({
domain: setup.domain,
type: "icon",
useFallback: true,
darkOptimized: this.hass.themes?.darkMode,
})}
referrerpolicy="no-referrer"
slot="graphic"
/>
<span>
${domainToName(this.hass.localize, setup.domain, manifest)}
</span>
<span slot="secondary">${setup.domain}</span>
<div slot="meta">
${setupSeconds ? html`${setupSeconds} s` : ""}
</div>
</ha-clickable-list-item>
`;
})}
</mwc-list>
</ha-card>
const setupSeconds = setup.seconds?.toFixed(2);
return html`
<ha-clickable-list-item
graphic="avatar"
twoline
hasMeta
openNewTab
@click=${this._entryClicked}
href=${docLink}
>
<img
loading="lazy"
src=${brandsUrl({
domain: setup.domain,
type: "icon",
useFallback: true,
darkOptimized: this.hass.themes?.darkMode,
})}
referrerpolicy="no-referrer"
slot="graphic"
/>
<span>
${domainToName(this.hass.localize, setup.domain, manifest)}
</span>
<span slot="secondary">${setup.domain}</span>
<div slot="meta">
${setupSeconds ? html`${setupSeconds} s` : ""}
</div>
</ha-clickable-list-item>
`;
})}
</mwc-list>
`;
}
@ -149,6 +139,6 @@ class IntegrationsCard extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"integrations-card": IntegrationsCard;
"integrations-startup-time": IntegrationsStartupTime;
}
}

View File

@ -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: {},
});
};

View File

@ -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,
});
};

View File

@ -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",