mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 17:56:46 +00:00
Move System Information to Repairs (#13281)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
f7090583ac
commit
c73677f15d
@ -26,7 +26,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
UNHEALTHY_REASON_URL,
|
UNHEALTHY_REASON_URL,
|
||||||
UNSUPPORTED_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 { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import { bytesToString } from "../../../src/util/bytes-to-string";
|
import { bytesToString } from "../../../src/util/bytes-to-string";
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
mdiCog,
|
mdiCog,
|
||||||
mdiDatabase,
|
mdiDatabase,
|
||||||
mdiDevices,
|
mdiDevices,
|
||||||
mdiHeart,
|
|
||||||
mdiInformation,
|
mdiInformation,
|
||||||
mdiInformationOutline,
|
mdiInformationOutline,
|
||||||
mdiLifebuoy,
|
mdiLifebuoy,
|
||||||
@ -322,13 +321,6 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
|||||||
iconColor: "#301A8E",
|
iconColor: "#301A8E",
|
||||||
component: "hassio",
|
component: "hassio",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/config/system_health",
|
|
||||||
translationKey: "system_health",
|
|
||||||
iconPath: mdiHeart,
|
|
||||||
iconColor: "#507FfE",
|
|
||||||
components: ["system_health", "hassio"],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
about: [
|
about: [
|
||||||
{
|
{
|
||||||
@ -447,10 +439,6 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
tag: "ha-config-section-storage",
|
tag: "ha-config-section-storage",
|
||||||
load: () => import("./storage/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: {
|
updates: {
|
||||||
tag: "ha-config-section-updates",
|
tag: "ha-config-section-updates",
|
||||||
load: () => import("./core/ha-config-section-updates"),
|
load: () => import("./core/ha-config-section-updates"),
|
||||||
|
57
src/panels/config/repairs/dialog-integration-startup.ts
Normal file
57
src/panels/config/repairs/dialog-integration-startup.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,15 @@
|
|||||||
import { ActionDetail } from "@material/mwc-list";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { mdiContentCopy } from "@mdi/js";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket/dist/types";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { formatDateTime } from "../../../common/datetime/format_date_time";
|
import { formatDateTime } from "../../../common/datetime/format_date_time";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||||
import { subscribePollingCollection } from "../../../common/util/subscribe-polling";
|
import { subscribePollingCollection } from "../../../common/util/subscribe-polling";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-button-menu";
|
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-circular-progress";
|
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||||
import "../../../components/ha-metric";
|
import "../../../components/ha-metric";
|
||||||
import { fetchHassioStats, HassioStats } from "../../../data/hassio/common";
|
import { fetchHassioStats, HassioStats } from "../../../data/hassio/common";
|
||||||
import {
|
import {
|
||||||
@ -25,12 +23,11 @@ import {
|
|||||||
SystemHealthInfo,
|
SystemHealthInfo,
|
||||||
} from "../../../data/system_health";
|
} from "../../../data/system_health";
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-subpage";
|
import { haStyleDialog } from "../../../resources/styles";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
import "./integrations-card";
|
import "../../../components/ha-circular-progress";
|
||||||
|
|
||||||
const sortKeys = (a: string, b: string) => {
|
const sortKeys = (a: string, b: string) => {
|
||||||
if (a === "homeassistant") {
|
if (a === "homeassistant") {
|
||||||
@ -53,28 +50,40 @@ export const UNHEALTHY_REASON_URL = {
|
|||||||
privileged: "/more-info/unsupported/privileged",
|
privileged: "/more-info/unsupported/privileged",
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("ha-config-system-health")
|
@customElement("dialog-system-information")
|
||||||
class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
|
class DialogSystemInformation extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@state() private _systemInfo?: SystemHealthInfo;
|
||||||
|
|
||||||
@state() private _info?: SystemHealthInfo;
|
|
||||||
|
|
||||||
@state() private _supervisorStats?: HassioStats;
|
|
||||||
|
|
||||||
@state() private _resolutionInfo?: HassioResolution;
|
@state() private _resolutionInfo?: HassioResolution;
|
||||||
|
|
||||||
|
@state() private _supervisorStats?: HassioStats;
|
||||||
|
|
||||||
@state() private _coreStats?: 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>> = [];
|
const subs: Array<UnsubscribeFunc | Promise<UnsubscribeFunc>> = [];
|
||||||
if (isComponentLoaded(this.hass, "system_health")) {
|
if (isComponentLoaded(this.hass, "system_health")) {
|
||||||
subs.push(
|
subs.push(
|
||||||
subscribeSystemHealthInfo(this.hass!, (info) => {
|
subscribeSystemHealthInfo(this.hass!, (info) => {
|
||||||
this._info = info;
|
this._systemInfo = info;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -93,149 +102,51 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
|
|||||||
10000
|
10000
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
fetchHassioResolution(this.hass).then((data) => {
|
fetchHassioResolution(this.hass).then((data) => {
|
||||||
this._resolutionInfo = data;
|
this._resolutionInfo = data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return subs;
|
this._subscriptions = subs;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
private _unsubscribe() {
|
||||||
super.firstUpdated(changedProps);
|
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 {
|
protected render(): TemplateResult {
|
||||||
const sections: TemplateResult[] = [];
|
if (!this._opened) {
|
||||||
|
return html``;
|
||||||
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>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sections = this._getSections();
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage
|
<ha-dialog
|
||||||
.hass=${this.hass}
|
open
|
||||||
.narrow=${this.narrow}
|
@closed=${this.closeDialog}
|
||||||
back-path="/config/system"
|
scrimClickAction
|
||||||
.header=${this.hass.localize("ui.panel.config.system_health.caption")}
|
escapeKeyAction
|
||||||
|
.heading=${createCloseHeading(
|
||||||
|
this.hass,
|
||||||
|
this.hass.localize("ui.panel.config.repairs.system_information")
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
${this._error
|
<div>
|
||||||
? 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">
|
|
||||||
${this._resolutionInfo
|
${this._resolutionInfo
|
||||||
? html`${this._resolutionInfo.unhealthy.length
|
? html`${this._resolutionInfo.unhealthy.length
|
||||||
? html`<ha-alert alert-type="error">
|
? html`<ha-alert alert-type="error">
|
||||||
@ -265,66 +176,63 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
|
|||||||
: ""} `
|
: ""} `
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-card outlined>
|
<div>${sections}</div>
|
||||||
<div class="card-content">${sections}</div>
|
|
||||||
</ha-card>
|
|
||||||
${!this._coreStats && !this._supervisorStats
|
${!this._coreStats && !this._supervisorStats
|
||||||
? ""
|
? ""
|
||||||
: html`
|
: html`
|
||||||
<ha-card outlined>
|
<div>
|
||||||
<div class="card-content">
|
${this._coreStats
|
||||||
${this._coreStats
|
? html`
|
||||||
? html`
|
<h3>
|
||||||
<h3>
|
${this.hass.localize(
|
||||||
${this.hass.localize(
|
"ui.panel.config.system_health.core_stats"
|
||||||
"ui.panel.config.system_health.core_stats"
|
)}
|
||||||
)}
|
</h3>
|
||||||
</h3>
|
<ha-metric
|
||||||
<ha-metric
|
.heading=${this.hass.localize(
|
||||||
.heading=${this.hass.localize(
|
"ui.panel.config.system_health.cpu_usage"
|
||||||
"ui.panel.config.system_health.cpu_usage"
|
)}
|
||||||
)}
|
.value=${this._coreStats.cpu_percent}
|
||||||
.value=${this._coreStats.cpu_percent}
|
></ha-metric>
|
||||||
></ha-metric>
|
<ha-metric
|
||||||
<ha-metric
|
.heading=${this.hass.localize(
|
||||||
.heading=${this.hass.localize(
|
"ui.panel.config.system_health.ram_usage"
|
||||||
"ui.panel.config.system_health.ram_usage"
|
)}
|
||||||
)}
|
.value=${this._coreStats.memory_percent}
|
||||||
.value=${this._coreStats.memory_percent}
|
></ha-metric>
|
||||||
></ha-metric>
|
`
|
||||||
`
|
: ""}
|
||||||
: ""}
|
${this._supervisorStats
|
||||||
${this._supervisorStats
|
? html`
|
||||||
? html`
|
<h3>
|
||||||
<h3>
|
${this.hass.localize(
|
||||||
${this.hass.localize(
|
"ui.panel.config.system_health.supervisor_stats"
|
||||||
"ui.panel.config.system_health.supervisor_stats"
|
)}
|
||||||
)}
|
</h3>
|
||||||
</h3>
|
<ha-metric
|
||||||
<ha-metric
|
.heading=${this.hass.localize(
|
||||||
.heading=${this.hass.localize(
|
"ui.panel.config.system_health.cpu_usage"
|
||||||
"ui.panel.config.system_health.cpu_usage"
|
)}
|
||||||
)}
|
.value=${this._supervisorStats.cpu_percent}
|
||||||
.value=${this._supervisorStats.cpu_percent}
|
></ha-metric>
|
||||||
></ha-metric>
|
<ha-metric
|
||||||
<ha-metric
|
.heading=${this.hass.localize(
|
||||||
.heading=${this.hass.localize(
|
"ui.panel.config.system_health.ram_usage"
|
||||||
"ui.panel.config.system_health.ram_usage"
|
)}
|
||||||
)}
|
.value=${this._supervisorStats.memory_percent}
|
||||||
.value=${this._supervisorStats.memory_percent}
|
></ha-metric>
|
||||||
></ha-metric>
|
`
|
||||||
`
|
: ""}
|
||||||
: ""}
|
</div>
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`}
|
`}
|
||||||
|
|
||||||
<integrations-card
|
|
||||||
.hass=${this.hass}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
></integrations-card>
|
|
||||||
</div>
|
</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> {
|
private _getSections(): TemplateResult[] {
|
||||||
const github = ev.detail.index === 1;
|
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;
|
let haContent: string | undefined;
|
||||||
const domainParts: string[] = [];
|
const domainParts: string[] = [];
|
||||||
|
|
||||||
for (const domain of Object.keys(this._info!).sort(sortKeys)) {
|
for (const domain of Object.keys(this._systemInfo!).sort(sortKeys)) {
|
||||||
const domainInfo = this._info![domain];
|
const domainInfo = this._systemInfo![domain];
|
||||||
let first = true;
|
let first = true;
|
||||||
const parts = [
|
const parts = [
|
||||||
`${
|
`${
|
||||||
github && domain !== "homeassistant"
|
domain !== "homeassistant"
|
||||||
? `<details><summary>${domainToName(
|
? `<details><summary>${domainToName(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
domain
|
domain
|
||||||
@ -408,7 +410,7 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
|
|||||||
for (const key of Object.keys(domainInfo.info)) {
|
for (const key of Object.keys(domainInfo.info)) {
|
||||||
let value: unknown;
|
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;
|
const info = domainInfo.info[key] as SystemCheckValueObject;
|
||||||
|
|
||||||
if (info.type === "pending") {
|
if (info.type === "pending") {
|
||||||
@ -421,11 +423,11 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
|
|||||||
} else {
|
} else {
|
||||||
value = domainInfo.info[key];
|
value = domainInfo.info[key];
|
||||||
}
|
}
|
||||||
if (github && first) {
|
if (first) {
|
||||||
parts.push(`${key} | ${value}\n-- | --`);
|
parts.push(`${key} | ${value}\n-- | --`);
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
parts.push(`${key}${github ? " | " : ": "}${value}`);
|
parts.push(`${key} | ${value}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,16 +435,14 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
|
|||||||
haContent = parts.join("\n");
|
haContent = parts.join("\n");
|
||||||
} else {
|
} else {
|
||||||
domainParts.push(parts.join("\n"));
|
domainParts.push(parts.join("\n"));
|
||||||
if (github && domain !== "homeassistant") {
|
if (domain !== "homeassistant") {
|
||||||
domainParts.push("</details>");
|
domainParts.push("</details>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await copyToClipboard(
|
await copyToClipboard(
|
||||||
`${github ? "## " : ""}System Health\n${haContent}\n\n${domainParts.join(
|
`${"## "}System Information\n${haContent}\n\n${domainParts.join("\n\n")}`
|
||||||
"\n\n"
|
|
||||||
)}`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
showToast(this, {
|
showToast(this, {
|
||||||
@ -450,73 +450,50 @@ class HaConfigSystemHealth extends SubscribeMixin(LitElement) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles: CSSResultGroup = css`
|
static styles: CSSResultGroup = [
|
||||||
.content {
|
haStyleDialog,
|
||||||
padding: 28px 20px 0;
|
css`
|
||||||
max-width: 1040px;
|
ha-alert {
|
||||||
margin: 0 auto;
|
margin-bottom: 16px;
|
||||||
}
|
display: block;
|
||||||
integrations-card {
|
}
|
||||||
max-width: 600px;
|
table {
|
||||||
display: block;
|
width: 100%;
|
||||||
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%;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:first-child {
|
td:first-child {
|
||||||
width: 45%;
|
width: 45%;
|
||||||
}
|
}
|
||||||
|
|
||||||
td:last-child {
|
td:last-child {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-container {
|
.loading-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a.manage {
|
||||||
color: var(--primary-color);
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
a.manage {
|
];
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-config-system-health": HaConfigSystemHealth;
|
"dialog-system-information": DialogSystemInformation;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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 { mdiDotsVertical } from "@mdi/js";
|
||||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
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 "../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
fetchRepairsIssues,
|
fetchRepairsIssues,
|
||||||
@ -10,11 +12,14 @@ import {
|
|||||||
severitySort,
|
severitySort,
|
||||||
} from "../../../data/repairs";
|
} from "../../../data/repairs";
|
||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "./ha-config-repairs";
|
import "./ha-config-repairs";
|
||||||
|
import { showIntegrationStartupDialog } from "./show-integration-startup-dialog";
|
||||||
|
import { showSystemInformationDialog } from "./show-system-information-dialog";
|
||||||
|
|
||||||
@customElement("ha-config-repairs-dashboard")
|
@customElement("ha-config-repairs-dashboard")
|
||||||
class HaConfigRepairsDashboard extends LitElement {
|
class HaConfigRepairsDashboard extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
@ -40,6 +45,7 @@ class HaConfigRepairsDashboard extends LitElement {
|
|||||||
this._showIgnored,
|
this._showIgnored,
|
||||||
this._repairsIssues
|
this._repairsIssues
|
||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-subpage
|
<hass-subpage
|
||||||
back-path="/config/system"
|
back-path="/config/system"
|
||||||
@ -48,13 +54,32 @@ class HaConfigRepairsDashboard extends LitElement {
|
|||||||
.header=${this.hass.localize("ui.panel.config.repairs.caption")}
|
.header=${this.hass.localize("ui.panel.config.repairs.caption")}
|
||||||
>
|
>
|
||||||
<div slot="toolbar-icon">
|
<div slot="toolbar-icon">
|
||||||
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
|
<ha-button-menu corner="BOTTOM_START">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></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._showIgnored
|
||||||
? this.hass.localize("ui.panel.config.repairs.hide_ignored")
|
? this.hass.localize("ui.panel.config.repairs.hide_ignored")
|
||||||
: this.hass.localize("ui.panel.config.repairs.show_ignored")}
|
: this.hass.localize("ui.panel.config.repairs.show_ignored")}
|
||||||
@ -98,12 +123,32 @@ class HaConfigRepairsDashboard extends LitElement {
|
|||||||
this.hass.loadBackendTranslation("issues", [...integrations]);
|
this.hass.loadBackendTranslation("issues", [...integrations]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleAction(ev: CustomEvent<ActionDetail>) {
|
private _showSystemInformationDialog(
|
||||||
switch (ev.detail.index) {
|
ev: CustomEvent<RequestSelectedDetail>
|
||||||
case 0:
|
): void {
|
||||||
this._showIgnored = !this._showIgnored;
|
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||||
break;
|
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`
|
static styles = css`
|
||||||
|
@ -21,8 +21,8 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import { brandsUrl } from "../../../util/brands-url";
|
import { brandsUrl } from "../../../util/brands-url";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
|
|
||||||
@customElement("integrations-card")
|
@customElement("integrations-startup-time")
|
||||||
class IntegrationsCard extends LitElement {
|
class IntegrationsStartupTime extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow = false;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
@ -45,57 +45,47 @@ class IntegrationsCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<mwc-list>
|
||||||
outlined
|
${this._setups?.map((setup) => {
|
||||||
.header=${this.hass.localize(
|
const manifest = this._manifests && this._manifests[setup.domain];
|
||||||
"ui.panel.config.system_health.integration_start_time"
|
const docLink = manifest
|
||||||
)}
|
? manifest.is_built_in
|
||||||
>
|
? documentationUrl(this.hass, `/integrations/${manifest.domain}`)
|
||||||
<mwc-list>
|
: 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);
|
const setupSeconds = setup.seconds?.toFixed(2);
|
||||||
return html`
|
return html`
|
||||||
<ha-clickable-list-item
|
<ha-clickable-list-item
|
||||||
graphic="avatar"
|
graphic="avatar"
|
||||||
twoline
|
twoline
|
||||||
hasMeta
|
hasMeta
|
||||||
openNewTab
|
openNewTab
|
||||||
@click=${this._entryClicked}
|
@click=${this._entryClicked}
|
||||||
href=${docLink}
|
href=${docLink}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
src=${brandsUrl({
|
src=${brandsUrl({
|
||||||
domain: setup.domain,
|
domain: setup.domain,
|
||||||
type: "icon",
|
type: "icon",
|
||||||
useFallback: true,
|
useFallback: true,
|
||||||
darkOptimized: this.hass.themes?.darkMode,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
${domainToName(this.hass.localize, setup.domain, manifest)}
|
${domainToName(this.hass.localize, setup.domain, manifest)}
|
||||||
</span>
|
</span>
|
||||||
<span slot="secondary">${setup.domain}</span>
|
<span slot="secondary">${setup.domain}</span>
|
||||||
<div slot="meta">
|
<div slot="meta">
|
||||||
${setupSeconds ? html`${setupSeconds} s` : ""}
|
${setupSeconds ? html`${setupSeconds} s` : ""}
|
||||||
</div>
|
</div>
|
||||||
</ha-clickable-list-item>
|
</ha-clickable-list-item>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</mwc-list>
|
</mwc-list>
|
||||||
</ha-card>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +139,6 @@ class IntegrationsCard extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"integrations-card": IntegrationsCard;
|
"integrations-startup-time": IntegrationsStartupTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
12
src/panels/config/repairs/show-integration-startup-dialog.ts
Normal file
12
src/panels/config/repairs/show-integration-startup-dialog.ts
Normal 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: {},
|
||||||
|
});
|
||||||
|
};
|
12
src/panels/config/repairs/show-system-information-dialog.ts
Normal file
12
src/panels/config/repairs/show-system-information-dialog.ts
Normal 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,
|
||||||
|
});
|
||||||
|
};
|
@ -1237,6 +1237,9 @@
|
|||||||
"critical": "Critical",
|
"critical": "Critical",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"warning": "Warning",
|
"warning": "Warning",
|
||||||
|
"system_information": "System information",
|
||||||
|
"integration_startup_time": "Integration startup time",
|
||||||
|
"copy": "Copy",
|
||||||
"dialog": {
|
"dialog": {
|
||||||
"title": "Repair",
|
"title": "Repair",
|
||||||
"fix": "Repair",
|
"fix": "Repair",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user