mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
move download logs button, switch between raw and normal logs (#22721)
This commit is contained in:
parent
125ad9c794
commit
387392713c
@ -47,7 +47,6 @@ class HassioAddonLogDashboard extends LitElement {
|
|||||||
.localizeFunc=${this.supervisor.localize}
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.header=${this.addon.name}
|
.header=${this.addon.name}
|
||||||
.provider=${this.addon.slug}
|
.provider=${this.addon.slug}
|
||||||
show
|
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
>
|
>
|
||||||
</error-log-card>
|
</error-log-card>
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
mdiWrap,
|
mdiWrap,
|
||||||
mdiWrapDisabled,
|
mdiWrapDisabled,
|
||||||
|
mdiFolderTextOutline,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
@ -58,7 +59,7 @@ import {
|
|||||||
downloadFileSupported,
|
downloadFileSupported,
|
||||||
fileDownload,
|
fileDownload,
|
||||||
} from "../../../util/file_download";
|
} from "../../../util/file_download";
|
||||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
import { fireEvent, type HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
import type { ConnectionStatus } from "../../../data/connection-status";
|
import type { ConnectionStatus } from "../../../data/connection-status";
|
||||||
import { atLeastVersion } from "../../../common/config/version";
|
import { atLeastVersion } from "../../../common/config/version";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
@ -79,9 +80,10 @@ class ErrorLogCard extends LitElement {
|
|||||||
|
|
||||||
@property() public header?: string;
|
@property() public header?: string;
|
||||||
|
|
||||||
@property() public provider!: string;
|
@property() public provider?: string;
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: true }) public show = false;
|
@property({ attribute: "allow-switch", type: Boolean }) public allowSwitch =
|
||||||
|
false;
|
||||||
|
|
||||||
@query(".error-log") private _logElement?: HTMLElement;
|
@query(".error-log") private _logElement?: HTMLElement;
|
||||||
|
|
||||||
@ -130,26 +132,32 @@ class ErrorLogCard extends LitElement {
|
|||||||
|
|
||||||
@state() private _wrapLines = true;
|
@state() private _wrapLines = true;
|
||||||
|
|
||||||
@state() private _downloadSupported;
|
@state() private _downloadSupported?: boolean;
|
||||||
|
|
||||||
@state() private _logsFileLink;
|
@state() private _logsFileLink?: string;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
|
const streaming =
|
||||||
|
this._streamSupported &&
|
||||||
|
this.provider &&
|
||||||
|
isComponentLoaded(this.hass, "hassio") &&
|
||||||
|
this._loadingState !== "loading";
|
||||||
|
|
||||||
|
const hasBoots = this._streamSupported && Array.isArray(this._boots);
|
||||||
|
|
||||||
const localize = this.localizeFunc || this.hass.localize;
|
const localize = this.localizeFunc || this.hass.localize;
|
||||||
return html`
|
return html`
|
||||||
<div class="error-log-intro">
|
<div class="error-log-intro">
|
||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-card outlined class=${classMap({ hidden: this.show === false })}>
|
<ha-card outlined>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1 class="card-header">
|
<h1 class="card-header">
|
||||||
${this.header || localize("ui.panel.config.logs.show_full_logs")}
|
${this.header || localize("ui.panel.config.logs.show_full_logs")}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
${this._streamSupported &&
|
${hasBoots && this._showBootsSelect
|
||||||
Array.isArray(this._boots) &&
|
|
||||||
this._showBootsSelect
|
|
||||||
? html`
|
? html`
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
.title=${localize(
|
.title=${localize(
|
||||||
@ -175,7 +183,7 @@ class ErrorLogCard extends LitElement {
|
|||||||
id="boots-menu"
|
id="boots-menu"
|
||||||
positioning="fixed"
|
positioning="fixed"
|
||||||
>
|
>
|
||||||
${this._boots.map(
|
${this._boots!.map(
|
||||||
(boot) => html`
|
(boot) => html`
|
||||||
<ha-md-menu-item
|
<ha-md-menu-item
|
||||||
.value=${boot}
|
.value=${boot}
|
||||||
@ -232,19 +240,31 @@ class ErrorLogCard extends LitElement {
|
|||||||
`ui.panel.config.logs.${this._wrapLines ? "full_width" : "wrap_lines"}`
|
`ui.panel.config.logs.${this._wrapLines ? "full_width" : "wrap_lines"}`
|
||||||
)}
|
)}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
${!this._streamSupported || this._error
|
${!streaming || this._error
|
||||||
? html`<ha-icon-button
|
? html`<ha-icon-button
|
||||||
.path=${mdiRefresh}
|
.path=${mdiRefresh}
|
||||||
@click=${this._loadLogs}
|
@click=${this._loadLogs}
|
||||||
.label=${localize("ui.common.refresh")}
|
.label=${localize("ui.common.refresh")}
|
||||||
></ha-icon-button>`
|
></ha-icon-button>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this._streamSupported && Array.isArray(this._boots)
|
${(this.allowSwitch && this.provider === "core") || hasBoots
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu @action=${this._handleOverflowAction}>
|
<ha-button-menu @action=${this._handleOverflowAction}>
|
||||||
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
||||||
</ha-icon-button>
|
</ha-icon-button>
|
||||||
<ha-list-item graphic="icon">
|
${this.allowSwitch && this.provider === "core"
|
||||||
|
? html`<ha-list-item graphic="icon">
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiFolderTextOutline}
|
||||||
|
></ha-svg-icon>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.logs.show_condensed_logs"
|
||||||
|
)}
|
||||||
|
</ha-list-item>`
|
||||||
|
: nothing}
|
||||||
|
${hasBoots
|
||||||
|
? html`<ha-list-item graphic="icon">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="graphic"
|
slot="graphic"
|
||||||
.path=${mdiFormatListNumbered}
|
.path=${mdiFormatListNumbered}
|
||||||
@ -252,7 +272,8 @@ class ErrorLogCard extends LitElement {
|
|||||||
${localize(
|
${localize(
|
||||||
`ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots`
|
`ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots`
|
||||||
)}
|
)}
|
||||||
</ha-list-item>
|
</ha-list-item>`
|
||||||
|
: nothing}
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
@ -305,48 +326,34 @@ class ErrorLogCard extends LitElement {
|
|||||||
slot="trailingIcon"
|
slot="trailingIcon"
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-button>
|
</ha-button>
|
||||||
${this._streamSupported &&
|
${streaming && this._boot === 0 && !this._error
|
||||||
this._loadingState !== "loading" &&
|
|
||||||
this._boot === 0 &&
|
|
||||||
!this._error
|
|
||||||
? html`<div class="live-indicator">
|
? html`<div class="live-indicator">
|
||||||
<ha-svg-icon path=${mdiCircle}></ha-svg-icon>
|
<ha-svg-icon path=${mdiCircle}></ha-svg-icon>
|
||||||
Live
|
Live
|
||||||
</div>`
|
</div>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
${this.show === false
|
|
||||||
? html`
|
|
||||||
${this._downloadSupported
|
|
||||||
? html`
|
|
||||||
<ha-button outlined @click=${this._downloadLogs}>
|
|
||||||
<ha-svg-icon .path=${mdiDownload}></ha-svg-icon>
|
|
||||||
${localize("ui.panel.config.logs.download_logs")}
|
|
||||||
</ha-button>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
<mwc-button raised @click=${this._showLogs}>
|
|
||||||
${localize("ui.panel.config.logs.load_logs")}
|
|
||||||
</mwc-button>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public connectedCallback() {
|
protected willUpdate(changedProps: PropertyValues) {
|
||||||
super.connectedCallback();
|
super.willUpdate(changedProps);
|
||||||
|
if (changedProps.has("provider")) {
|
||||||
if (this._streamSupported === undefined) {
|
this._boot = 0;
|
||||||
this._streamSupported = atLeastVersion(
|
this._loadLogs();
|
||||||
this.hass.config.version,
|
|
||||||
2024,
|
|
||||||
11
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (this._downloadSupported === undefined && this.hass) {
|
if (this.hasUpdated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._streamSupported = atLeastVersion(this.hass.config.version, 2024, 11);
|
||||||
this._downloadSupported = downloadFileSupported(this.hass);
|
this._downloadSupported = downloadFileSupported(this.hass);
|
||||||
}
|
// just needs to be loaded once, because only the host endpoints provide boots information
|
||||||
|
this._loadBoots();
|
||||||
|
|
||||||
|
window.addEventListener("connection-status", this._handleConnectionStatus);
|
||||||
|
|
||||||
|
this.hass.loadFragmentTranslation("config");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
@ -356,28 +363,11 @@ class ErrorLogCard extends LitElement {
|
|||||||
|
|
||||||
this._scrolledToTopController.callback = this._handleTopScroll;
|
this._scrolledToTopController.callback = this._handleTopScroll;
|
||||||
this._scrolledToTopController.observe(this._scrollTopMarkerElement!);
|
this._scrolledToTopController.observe(this._scrollTopMarkerElement!);
|
||||||
|
|
||||||
window.addEventListener("connection-status", this._handleConnectionStatus);
|
|
||||||
|
|
||||||
if (this.hass?.config.recovery_mode || this.show) {
|
|
||||||
this.hass.loadFragmentTranslation("config");
|
|
||||||
}
|
|
||||||
|
|
||||||
// just needs to be loaded once, because only the host endpoints provide boots information
|
|
||||||
this._loadBoots();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps) {
|
protected updated(changedProps) {
|
||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
|
|
||||||
if (
|
|
||||||
(changedProps.has("show") && this.show) ||
|
|
||||||
(changedProps.has("provider") && this.show)
|
|
||||||
) {
|
|
||||||
this._boot = 0;
|
|
||||||
this._loadLogs();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._newLogsIndicator && this._scrolledToBottomController.value) {
|
if (this._newLogsIndicator && this._scrolledToBottomController.value) {
|
||||||
this._newLogsIndicator = false;
|
this._newLogsIndicator = false;
|
||||||
}
|
}
|
||||||
@ -411,7 +401,7 @@ class ErrorLogCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _downloadLogs(): Promise<void> {
|
private async _downloadLogs(): Promise<void> {
|
||||||
if (this._streamSupported) {
|
if (this._streamSupported && this.provider) {
|
||||||
showDownloadLogsDialog(this, {
|
showDownloadLogsDialog(this, {
|
||||||
header: this.header,
|
header: this.header,
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
@ -433,10 +423,6 @@ class ErrorLogCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showLogs(): void {
|
|
||||||
this.show = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _loadLogs(): Promise<void> {
|
private async _loadLogs(): Promise<void> {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._loadingState = "loading";
|
this._loadingState = "loading";
|
||||||
@ -448,15 +434,16 @@ class ErrorLogCard extends LitElement {
|
|||||||
try {
|
try {
|
||||||
if (this._logStreamAborter) {
|
if (this._logStreamAborter) {
|
||||||
this._logStreamAborter.abort();
|
this._logStreamAborter.abort();
|
||||||
|
this._logStreamAborter = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._logStreamAborter = new AbortController();
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this._streamSupported &&
|
this._streamSupported &&
|
||||||
isComponentLoaded(this.hass, "hassio") &&
|
isComponentLoaded(this.hass, "hassio") &&
|
||||||
this.provider
|
this.provider
|
||||||
) {
|
) {
|
||||||
|
this._logStreamAborter = new AbortController();
|
||||||
|
|
||||||
// check if there are any logs at all
|
// check if there are any logs at all
|
||||||
const testResponse = await fetchHassioLogs(
|
const testResponse = await fetchHassioLogs(
|
||||||
this.hass,
|
this.hass,
|
||||||
@ -599,18 +586,21 @@ class ErrorLogCard extends LitElement {
|
|||||||
if (ev.detail === "disconnected" && this._logStreamAborter) {
|
if (ev.detail === "disconnected" && this._logStreamAborter) {
|
||||||
this._logStreamAborter.abort();
|
this._logStreamAborter.abort();
|
||||||
}
|
}
|
||||||
if (ev.detail === "connected" && this.show) {
|
if (ev.detail === "connected") {
|
||||||
this._loadLogs();
|
this._loadLogs();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private async _loadMoreLogs() {
|
private async _loadMoreLogs() {
|
||||||
if (
|
if (
|
||||||
this._firstCursor &&
|
!this._firstCursor ||
|
||||||
this._loadingPrevState !== "loading" &&
|
this._loadingPrevState === "loading" ||
|
||||||
this._loadingState === "loaded" &&
|
this._loadingState !== "loaded" ||
|
||||||
this._logElement
|
!this._logElement ||
|
||||||
|
!this.provider
|
||||||
) {
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const scrolledToBottom = this._scrolledToBottomController.value;
|
const scrolledToBottom = this._scrolledToBottomController.value;
|
||||||
const scrollPositionFromBottom =
|
const scrollPositionFromBottom =
|
||||||
this._logElement.scrollHeight - this._logElement.scrollTop;
|
this._logElement.scrollHeight - this._logElement.scrollTop;
|
||||||
@ -654,7 +644,6 @@ class ErrorLogCard extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private _handleTopScroll = (entries) => {
|
private _handleTopScroll = (entries) => {
|
||||||
const isVisible = entries[0].isIntersecting;
|
const isVisible = entries[0].isIntersecting;
|
||||||
@ -694,7 +683,15 @@ class ErrorLogCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleOverflowAction(ev: CustomEvent<ActionDetail>) {
|
private _handleOverflowAction(ev: CustomEvent<ActionDetail>) {
|
||||||
switch (ev.detail.index) {
|
let index = ev.detail.index;
|
||||||
|
if (this.provider === "core") {
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
switch (index) {
|
||||||
|
case -1:
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "switch-log-view");
|
||||||
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
this._showBootsSelect = !this._showBootsSelect;
|
this._showBootsSelect = !this._showBootsSelect;
|
||||||
break;
|
break;
|
||||||
|
@ -3,20 +3,20 @@ import type { CSSResultGroup, TemplateResult } from "lit";
|
|||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
|
import { navigate } from "../../../common/navigate";
|
||||||
import { extractSearchParam } from "../../../common/url/search-params";
|
import { extractSearchParam } from "../../../common/url/search-params";
|
||||||
import "../../../components/ha-button-menu";
|
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/search-input";
|
import "../../../components/search-input";
|
||||||
import type { LogProvider } from "../../../data/error_log";
|
import type { LogProvider } from "../../../data/error_log";
|
||||||
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
|
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
|
||||||
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { HomeAssistant, Route } from "../../../types";
|
import type { HomeAssistant, Route } from "../../../types";
|
||||||
import "./error-log-card";
|
import "./error-log-card";
|
||||||
import "./system-log-card";
|
import "./system-log-card";
|
||||||
import type { SystemLogCard } from "./system-log-card";
|
import type { SystemLogCard } from "./system-log-card";
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
|
||||||
import { navigate } from "../../../common/navigate";
|
|
||||||
|
|
||||||
const logProviders: LogProvider[] = [
|
const logProviders: LogProvider[] = [
|
||||||
{
|
{
|
||||||
@ -57,6 +57,8 @@ export class HaConfigLogs extends LitElement {
|
|||||||
|
|
||||||
@state() private _filter = extractSearchParam("filter") || "";
|
@state() private _filter = extractSearchParam("filter") || "";
|
||||||
|
|
||||||
|
@state() private _detail = false;
|
||||||
|
|
||||||
@query("system-log-card") private systemLog?: SystemLogCard;
|
@query("system-log-card") private systemLog?: SystemLogCard;
|
||||||
|
|
||||||
@state() private _selectedLogProvider = "core";
|
@state() private _selectedLogProvider = "core";
|
||||||
@ -141,7 +143,7 @@ export class HaConfigLogs extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
${search}
|
${search}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this._selectedLogProvider === "core"
|
${this._selectedLogProvider === "core" && !this._detail
|
||||||
? html`
|
? html`
|
||||||
<system-log-card
|
<system-log-card
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -149,23 +151,28 @@ export class HaConfigLogs extends LitElement {
|
|||||||
(p) => p.key === this._selectedLogProvider
|
(p) => p.key === this._selectedLogProvider
|
||||||
)!.name}
|
)!.name}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
|
@switch-log-view=${this._showDetail}
|
||||||
></system-log-card>
|
></system-log-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: html`<error-log-card
|
||||||
<error-log-card
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.header=${this._logProviders.find(
|
.header=${this._logProviders.find(
|
||||||
(p) => p.key === this._selectedLogProvider
|
(p) => p.key === this._selectedLogProvider
|
||||||
)!.name}
|
)!.name}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
.provider=${this._selectedLogProvider}
|
.provider=${this._selectedLogProvider}
|
||||||
.show=${this._selectedLogProvider !== "core"}
|
@switch-log-view=${this._showDetail}
|
||||||
></error-log-card>
|
allow-switch
|
||||||
|
></error-log-card>`}
|
||||||
</div>
|
</div>
|
||||||
</hass-subpage>
|
</hass-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _showDetail() {
|
||||||
|
this._detail = !this._detail;
|
||||||
|
}
|
||||||
|
|
||||||
private _selectProvider(ev) {
|
private _selectProvider(ev) {
|
||||||
this._selectedLogProvider = (ev.currentTarget as any).provider;
|
this._selectedLogProvider = (ev.currentTarget as any).provider;
|
||||||
this._filter = "";
|
this._filter = "";
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import { mdiRefresh } from "@mdi/js";
|
|
||||||
import "@material/mwc-list/mwc-list";
|
import "@material/mwc-list/mwc-list";
|
||||||
|
import { mdiDotsVertical, mdiDownload, mdiRefresh, mdiText } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } 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 { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import "../../../components/buttons/ha-call-service-button";
|
import "../../../components/buttons/ha-call-service-button";
|
||||||
import "../../../components/buttons/ha-progress-button";
|
import "../../../components/buttons/ha-progress-button";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-list-item";
|
import "../../../components/ha-list-item";
|
||||||
|
import { getSignedPath } from "../../../data/auth";
|
||||||
|
import { getErrorLogDownloadUrl } from "../../../data/error_log";
|
||||||
import { domainToName } from "../../../data/integration";
|
import { domainToName } from "../../../data/integration";
|
||||||
import type { LoggedError } from "../../../data/system_log";
|
import type { LoggedError } from "../../../data/system_log";
|
||||||
import {
|
import {
|
||||||
@ -19,6 +23,7 @@ import {
|
|||||||
isCustomIntegrationError,
|
isCustomIntegrationError,
|
||||||
} from "../../../data/system_log";
|
} from "../../../data/system_log";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import { fileDownload } from "../../../util/file_download";
|
||||||
import { showSystemLogDetailDialog } from "./show-dialog-system-log-detail";
|
import { showSystemLogDetailDialog } from "./show-dialog-system-log-detail";
|
||||||
import { formatSystemLogTime } from "./util";
|
import { formatSystemLogTime } from "./util";
|
||||||
|
|
||||||
@ -104,11 +109,34 @@ export class SystemLogCard extends LitElement {
|
|||||||
: html`
|
: html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1 class="card-header">${this.header || "Logs"}</h1>
|
<h1 class="card-header">${this.header || "Logs"}</h1>
|
||||||
|
<div class="header-buttons">
|
||||||
|
<ha-icon-button
|
||||||
|
.path=${mdiDownload}
|
||||||
|
@click=${this._downloadLogs}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.logs.download_logs"
|
||||||
|
)}
|
||||||
|
></ha-icon-button>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiRefresh}
|
.path=${mdiRefresh}
|
||||||
@click=${this.fetchData}
|
@click=${this.fetchData}
|
||||||
.label=${this.hass.localize("ui.common.refresh")}
|
.label=${this.hass.localize("ui.common.refresh")}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
|
||||||
|
<ha-button-menu @action=${this._handleOverflowAction}>
|
||||||
|
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
||||||
|
</ha-icon-button>
|
||||||
|
<ha-list-item graphic="icon">
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiText}
|
||||||
|
></ha-svg-icon>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.logs.show_full_logs"
|
||||||
|
)}
|
||||||
|
</ha-list-item>
|
||||||
|
</ha-button-menu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this._items.length === 0
|
${this._items.length === 0
|
||||||
? html`
|
? html`
|
||||||
@ -195,6 +223,19 @@ export class SystemLogCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleOverflowAction() {
|
||||||
|
// @ts-ignore
|
||||||
|
fireEvent(this, "switch-log-view");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _downloadLogs() {
|
||||||
|
const timeString = new Date().toISOString().replace(/:/g, "-");
|
||||||
|
const downloadUrl = getErrorLogDownloadUrl;
|
||||||
|
const logFileName = `home-assistant_${timeString}.log`;
|
||||||
|
const signedUrl = await getSignedPath(this.hass, downloadUrl);
|
||||||
|
fileDownload(signedUrl.path, logFileName);
|
||||||
|
}
|
||||||
|
|
||||||
private _openLog(ev: Event): void {
|
private _openLog(ev: Event): void {
|
||||||
const item = (ev.currentTarget as any).logItem;
|
const item = (ev.currentTarget as any).logItem;
|
||||||
showSystemLogDetailDialog(this, { item });
|
showSystemLogDetailDialog(this, { item });
|
||||||
@ -203,7 +244,7 @@ export class SystemLogCard extends LitElement {
|
|||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
ha-card {
|
ha-card {
|
||||||
padding-top: 16px;
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@ -212,6 +253,11 @@ export class SystemLogCard extends LitElement {
|
|||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-buttons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
color: var(--ha-card-header-color, var(--primary-text-color));
|
color: var(--ha-card-header-color, var(--primary-text-color));
|
||||||
font-family: var(--ha-card-header-font-family, inherit);
|
font-family: var(--ha-card-header-font-family, inherit);
|
||||||
@ -243,6 +289,10 @@ export class SystemLogCard extends LitElement {
|
|||||||
color: var(--warning-color);
|
color: var(--warning-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
border-top: 1px solid var(--divider-color);
|
||||||
|
}
|
||||||
|
|
||||||
.card-actions,
|
.card-actions,
|
||||||
.empty-content {
|
.empty-content {
|
||||||
direction: var(--direction);
|
direction: var(--direction);
|
||||||
|
@ -31,7 +31,7 @@ export class HuiRecoveryModeCard extends LitElement implements LovelaceCard {
|
|||||||
"ui.panel.lovelace.cards.recovery-mode.description"
|
"ui.panel.lovelace.cards.recovery-mode.description"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<error-log-card .hass=${this.hass}></error-log-card>
|
<error-log-card .hass=${this.hass} provider="core"></error-log-card>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -2470,9 +2470,9 @@
|
|||||||
"search": "Search logs",
|
"search": "Search logs",
|
||||||
"failed_get_logs": "Failed to get {provider} logs, {error}",
|
"failed_get_logs": "Failed to get {provider} logs, {error}",
|
||||||
"no_issues_search": "No issues found for search term ''{term}''",
|
"no_issues_search": "No issues found for search term ''{term}''",
|
||||||
"load_logs": "Load full logs",
|
"load_logs": "Load logs",
|
||||||
"nr_of_lines": "Number of lines",
|
"nr_of_lines": "Number of lines",
|
||||||
"loading_log": "Loading full log…",
|
"loading_log": "Loading log…",
|
||||||
"no_errors": "No errors have been reported",
|
"no_errors": "No errors have been reported",
|
||||||
"no_issues": "There are no new issues!",
|
"no_issues": "There are no new issues!",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
@ -2489,7 +2489,8 @@
|
|||||||
},
|
},
|
||||||
"custom_integration": "custom integration",
|
"custom_integration": "custom integration",
|
||||||
"error_from_custom_integration": "This error originated from a custom integration.",
|
"error_from_custom_integration": "This error originated from a custom integration.",
|
||||||
"show_full_logs": "Show full logs",
|
"show_full_logs": "Show raw logs",
|
||||||
|
"show_condensed_logs": "Show condensed logs",
|
||||||
"select_number_of_lines": "Select number of lines to download",
|
"select_number_of_lines": "Select number of lines to download",
|
||||||
"lines": "Lines",
|
"lines": "Lines",
|
||||||
"download_logs": "Download logs",
|
"download_logs": "Download logs",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user