Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/data/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export interface IntegrationManifest {
| "local_push";
}

export interface IntegrationSetup {
domain: string;
seconds?: number;
}

export const integrationIssuesUrl = (
domain: string,
manifest: IntegrationManifest
Expand All @@ -44,3 +49,6 @@ export const fetchIntegrationManifest = (
hass: HomeAssistant,
integration: string
) => hass.callWS<IntegrationManifest>({ type: "manifest/get", integration });

export const fetchIntegrationSetups = (hass: HomeAssistant) =>
hass.callWS<IntegrationSetup[]>({ type: "integration/setup_info" });
5 changes: 4 additions & 1 deletion src/panels/config/info/ha-config-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ class HaConfigInfo extends LitElement {
</div>
<div class="content">
<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>
</hass-tabs-subpage>
`;
Expand Down
120 changes: 89 additions & 31 deletions src/panels/config/info/integrations-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import "../../../components/ha-card";
import {
domainToName,
fetchIntegrationManifests,
fetchIntegrationSetups,
integrationIssuesUrl,
IntegrationManifest,
IntegrationSetup,
} from "../../../data/integration";
import { HomeAssistant } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
Expand All @@ -23,22 +25,30 @@ import { brandsUrl } from "../../../util/brands-url";
class IntegrationsCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;

@property({ type: Boolean }) public narrow = false;

@internalProperty() private _manifests?: {
[domain: string]: IntegrationManifest;
};

@internalProperty() private _setups?: {
[domain: string]: IntegrationSetup;
};

private _sortedIntegrations = memoizeOne((components: string[]) => {
return Array.from(
new Set(
components
.map((comp) => (comp.includes(".") ? comp.split(".")[1] : comp))
components.map((comp) =>
comp.includes(".") ? comp.split(".")[1] : comp
)
)
).sort();
});

firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._fetchManifests();
this._fetchSetups();
}

protected render(): TemplateResult {
Expand All @@ -47,10 +57,47 @@ class IntegrationsCard extends LitElement {
.header=${this.hass.localize("ui.panel.config.info.integrations")}
>
<table class="card-content">
<thead>
<tr>
<th></th>
${!this.narrow
? html`<th></th>
<th></th>
<th></th>`
: ""}
<th>Setup time</th>
</tr>
</thead>
<tbody>
${this._sortedIntegrations(this.hass!.config.components).map(
(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`
<tr>
<td>
Expand All @@ -63,39 +110,25 @@ class IntegrationsCard extends LitElement {
<td class="name">
${domainToName(this.hass.localize, domain, manifest)}<br />
<span class="domain">${domain}</span>
${this.narrow
? html`<div class="mobile-row">
<div>${docLink} ${issueLink}</div>
${setupSeconds ? html`${setupSeconds}s` : ""}
</div>`
: ""}
</td>
${!manifest
${this.narrow
? ""
: html`
<td>
<a
href=${manifest.documentation}
target="_blank"
rel="noreferrer"
>
${this.hass.localize(
"ui.panel.config.info.documentation"
)}
</a>
${docLink}
</td>
<td>
${issueLink}
</td>
<td class="setup">
${setupSeconds ? html`${setupSeconds}s` : ""}
</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>
`;
Expand All @@ -115,9 +148,21 @@ class IntegrationsCard extends LitElement {
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 {
return css`
td {
table {
width: 100%;
}
td,
th {
padding: 0 8px;
}
td:first-child {
Expand All @@ -126,9 +171,22 @@ class IntegrationsCard extends LitElement {
td.name {
padding: 8px;
}
td.setup {
text-align: right;
}
th {
text-align: right;
}
.domain {
color: var(--secondary-text-color);
}
.mobile-row {
display: flex;
justify-content: space-between;
}
.mobile-row a:not(:last-of-type) {
margin-right: 4px;
}
img {
display: block;
max-height: 40px;
Expand Down