Display cumulative setup seconds on the config info page (#8720)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
This commit is contained in:
J. Nick Koston 2021-04-27 11:38:29 -10:00 committed by GitHub
parent 93a1adaa56
commit 0c12586019
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 30 deletions

View File

@ -24,6 +24,11 @@ export interface IntegrationManifest {
| "local_push"; | "local_push";
} }
export interface IntegrationSetup {
domain: string;
seconds?: number;
}
export const integrationIssuesUrl = ( export const integrationIssuesUrl = (
domain: string, domain: string,
manifest: IntegrationManifest manifest: IntegrationManifest
@ -44,3 +49,6 @@ export const fetchIntegrationManifest = (
hass: HomeAssistant, hass: HomeAssistant,
integration: string integration: string
) => hass.callWS<IntegrationManifest>({ type: "manifest/get", integration }); ) => hass.callWS<IntegrationManifest>({ type: "manifest/get", integration });
export const fetchIntegrationSetups = (hass: HomeAssistant) =>
hass.callWS<IntegrationSetup[]>({ type: "integration/setup_info" });

View File

@ -140,7 +140,10 @@ class HaConfigInfo extends LitElement {
</div> </div>
<div class="content"> <div class="content">
<system-health-card .hass=${this.hass}></system-health-card> <system-health-card .hass=${this.hass}></system-health-card>
<integrations-card .hass=${this.hass}></integrations-card> <integrations-card
.hass=${this.hass}
.narrow=${this.narrow}
></integrations-card>
</div> </div>
</hass-tabs-subpage> </hass-tabs-subpage>
`; `;

View File

@ -13,8 +13,10 @@ import "../../../components/ha-card";
import { import {
domainToName, domainToName,
fetchIntegrationManifests, fetchIntegrationManifests,
fetchIntegrationSetups,
integrationIssuesUrl, integrationIssuesUrl,
IntegrationManifest, IntegrationManifest,
IntegrationSetup,
} from "../../../data/integration"; } from "../../../data/integration";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { brandsUrl } from "../../../util/brands-url"; import { brandsUrl } from "../../../util/brands-url";
@ -23,10 +25,16 @@ import { brandsUrl } from "../../../util/brands-url";
class IntegrationsCard extends LitElement { class IntegrationsCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow = false;
@internalProperty() private _manifests?: { @internalProperty() private _manifests?: {
[domain: string]: IntegrationManifest; [domain: string]: IntegrationManifest;
}; };
@internalProperty() private _setups?: {
[domain: string]: IntegrationSetup;
};
private _sortedIntegrations = memoizeOne((components: string[]) => { private _sortedIntegrations = memoizeOne((components: string[]) => {
return Array.from( return Array.from(
new Set( new Set(
@ -40,6 +48,7 @@ class IntegrationsCard extends LitElement {
firstUpdated(changedProps) { firstUpdated(changedProps) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
this._fetchManifests(); this._fetchManifests();
this._fetchSetups();
} }
protected render(): TemplateResult { protected render(): TemplateResult {
@ -48,10 +57,47 @@ class IntegrationsCard extends LitElement {
.header=${this.hass.localize("ui.panel.config.info.integrations")} .header=${this.hass.localize("ui.panel.config.info.integrations")}
> >
<table class="card-content"> <table class="card-content">
<thead>
<tr>
<th></th>
${!this.narrow
? html`<th></th>
<th></th>
<th></th>`
: ""}
<th>Setup time</th>
</tr>
</thead>
<tbody> <tbody>
${this._sortedIntegrations(this.hass!.config.components).map( ${this._sortedIntegrations(this.hass!.config.components).map(
(domain) => { (domain) => {
const manifest = this._manifests && this._manifests[domain]; const manifest = this._manifests && this._manifests[domain];
const docLink = manifest
? html`<a
href=${manifest.documentation}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.info.documentation"
)}</a
>`
: "";
const issueLink =
manifest && (manifest.is_built_in || manifest.issue_tracker)
? html`
<a
href=${integrationIssuesUrl(domain, manifest)}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.info.issues"
)}</a
>
`
: "";
const setupSeconds = this._setups?.[domain]?.seconds?.toFixed(
2
);
return html` return html`
<tr> <tr>
<td> <td>
@ -64,39 +110,25 @@ class IntegrationsCard extends LitElement {
<td class="name"> <td class="name">
${domainToName(this.hass.localize, domain, manifest)}<br /> ${domainToName(this.hass.localize, domain, manifest)}<br />
<span class="domain">${domain}</span> <span class="domain">${domain}</span>
${this.narrow
? html`<div class="mobile-row">
<div>${docLink} ${issueLink}</div>
${setupSeconds ? html`${setupSeconds}s` : ""}
</div>`
: ""}
</td> </td>
${!manifest ${this.narrow
? "" ? ""
: html` : html`
<td> <td>
<a ${docLink}
href=${manifest.documentation} </td>
target="_blank" <td>
rel="noreferrer" ${issueLink}
> </td>
${this.hass.localize( <td class="setup">
"ui.panel.config.info.documentation" ${setupSeconds ? html`${setupSeconds}s` : ""}
)}
</a>
</td> </td>
${manifest.is_built_in || manifest.issue_tracker
? html`
<td>
<a
href=${integrationIssuesUrl(
domain,
manifest
)}
target="_blank"
rel="noreferrer"
>
${this.hass.localize(
"ui.panel.config.info.issues"
)}
</a>
</td>
`
: ""}
`} `}
</tr> </tr>
`; `;
@ -116,9 +148,21 @@ class IntegrationsCard extends LitElement {
this._manifests = manifests; this._manifests = manifests;
} }
private async _fetchSetups() {
const setups = {};
for (const setup of await fetchIntegrationSetups(this.hass)) {
setups[setup.domain] = setup;
}
this._setups = setups;
}
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
td { table {
width: 100%;
}
td,
th {
padding: 0 8px; padding: 0 8px;
} }
td:first-child { td:first-child {
@ -127,9 +171,22 @@ class IntegrationsCard extends LitElement {
td.name { td.name {
padding: 8px; padding: 8px;
} }
td.setup {
text-align: right;
}
th {
text-align: right;
}
.domain { .domain {
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
.mobile-row {
display: flex;
justify-content: space-between;
}
.mobile-row a:not(:last-of-type) {
margin-right: 4px;
}
img { img {
display: block; display: block;
max-height: 40px; max-height: 40px;