mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Add select prev boots in error-log-card (#22528)
* Add select prev boots in error-log-card * Fix boot selector style in error-log-card
This commit is contained in:
parent
3180747a0a
commit
dc0cab9307
@ -65,6 +65,10 @@ export type HassioInfo = {
|
|||||||
timezone: string;
|
timezone: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type HassioBoots = {
|
||||||
|
boots: Record<number, string>;
|
||||||
|
};
|
||||||
|
|
||||||
export type HassioPanelInfo = PanelInfo<
|
export type HassioPanelInfo = PanelInfo<
|
||||||
| undefined
|
| undefined
|
||||||
| {
|
| {
|
||||||
@ -177,14 +181,18 @@ export const fetchHassioInfo = async (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const fetchHassioBoots = async (hass: HomeAssistant) =>
|
||||||
|
hass.callApi<HassioResponse<HassioBoots>>("GET", `hassio/host/logs/boots`);
|
||||||
|
|
||||||
export const fetchHassioLogs = async (
|
export const fetchHassioLogs = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
provider: string,
|
provider: string,
|
||||||
range?: string
|
range?: string,
|
||||||
|
boot = 0
|
||||||
) =>
|
) =>
|
||||||
hass.callApiRaw(
|
hass.callApiRaw(
|
||||||
"GET",
|
"GET",
|
||||||
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs`,
|
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/boots/${boot}`,
|
||||||
undefined,
|
undefined,
|
||||||
range
|
range
|
||||||
? {
|
? {
|
||||||
@ -197,11 +205,12 @@ export const fetchHassioLogsFollow = async (
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
provider: string,
|
provider: string,
|
||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
lines = 100
|
lines = 100,
|
||||||
|
boot = 0
|
||||||
) =>
|
) =>
|
||||||
hass.callApiRaw(
|
hass.callApiRaw(
|
||||||
"GET",
|
"GET",
|
||||||
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/follow?lines=${lines}`,
|
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/boots/${boot}/follow?lines=${lines}`,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
signal
|
signal
|
||||||
@ -212,10 +221,14 @@ export const getHassioLogDownloadUrl = (provider: string) =>
|
|||||||
provider.includes("_") ? `addons/${provider}` : provider
|
provider.includes("_") ? `addons/${provider}` : provider
|
||||||
}/logs`;
|
}/logs`;
|
||||||
|
|
||||||
export const getHassioLogDownloadLinesUrl = (provider: string, lines: number) =>
|
export const getHassioLogDownloadLinesUrl = (
|
||||||
|
provider: string,
|
||||||
|
lines: number,
|
||||||
|
boot = 0
|
||||||
|
) =>
|
||||||
`/api/hassio/${
|
`/api/hassio/${
|
||||||
provider.includes("_") ? `addons/${provider}` : provider
|
provider.includes("_") ? `addons/${provider}` : provider
|
||||||
}/logs?lines=${lines}`;
|
}/logs/boots/${boot}?lines=${lines}`;
|
||||||
|
|
||||||
export const setSupervisorOption = async (
|
export const setSupervisorOption = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -65,7 +65,11 @@ class DownloadLogsDialog extends LitElement {
|
|||||||
<span slot="title" id="dialog-light-color-favorite-title">
|
<span slot="title" id="dialog-light-color-favorite-title">
|
||||||
${this.hass.localize("ui.panel.config.logs.download_full_log")}
|
${this.hass.localize("ui.panel.config.logs.download_full_log")}
|
||||||
</span>
|
</span>
|
||||||
<span slot="subtitle"> ${this._dialogParams.header} </span>
|
<span slot="subtitle">
|
||||||
|
${this._dialogParams.header}${this._dialogParams.boot === 0
|
||||||
|
? ""
|
||||||
|
: ` ⸱ ${this._dialogParams.boot === -1 ? this.hass.localize("ui.panel.config.logs.previous") : this.hass.localize("ui.panel.config.logs.startups_ago", { boot: this._dialogParams.boot * -1 })}`}
|
||||||
|
</span>
|
||||||
</ha-dialog-header>
|
</ha-dialog-header>
|
||||||
<div slot="content" class="content">
|
<div slot="content" class="content">
|
||||||
<div>
|
<div>
|
||||||
@ -104,9 +108,14 @@ class DownloadLogsDialog extends LitElement {
|
|||||||
|
|
||||||
private async _dowloadLogs() {
|
private async _dowloadLogs() {
|
||||||
const provider = this._dialogParams!.provider;
|
const provider = this._dialogParams!.provider;
|
||||||
|
const boot = this._dialogParams!.boot;
|
||||||
|
|
||||||
const timeString = new Date().toISOString().replace(/:/g, "-");
|
const timeString = new Date().toISOString().replace(/:/g, "-");
|
||||||
const downloadUrl = getHassioLogDownloadLinesUrl(provider, this._lineCount);
|
const downloadUrl = getHassioLogDownloadLinesUrl(
|
||||||
|
provider,
|
||||||
|
this._lineCount,
|
||||||
|
boot
|
||||||
|
);
|
||||||
const logFileName =
|
const logFileName =
|
||||||
provider !== "core"
|
provider !== "core"
|
||||||
? `${provider}_${timeString}.log`
|
? `${provider}_${timeString}.log`
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiArrowCollapseDown, mdiDownload, mdiRefresh } from "@mdi/js";
|
import {
|
||||||
|
mdiArrowCollapseDown,
|
||||||
|
mdiDownload,
|
||||||
|
mdiMenuDown,
|
||||||
|
mdiRefresh,
|
||||||
|
} from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -22,12 +27,17 @@ import "../../../components/ha-button";
|
|||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
|
import "../../../components/chips/ha-assist-chip";
|
||||||
|
import "../../../components/ha-menu";
|
||||||
|
import "../../../components/ha-md-menu-item";
|
||||||
|
import "../../../components/ha-md-divider";
|
||||||
|
|
||||||
import { getSignedPath } from "../../../data/auth";
|
import { getSignedPath } from "../../../data/auth";
|
||||||
|
|
||||||
import { fetchErrorLog, getErrorLogDownloadUrl } from "../../../data/error_log";
|
import { fetchErrorLog, getErrorLogDownloadUrl } from "../../../data/error_log";
|
||||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||||
import {
|
import {
|
||||||
|
fetchHassioBoots,
|
||||||
fetchHassioLogs,
|
fetchHassioLogs,
|
||||||
fetchHassioLogsFollow,
|
fetchHassioLogsFollow,
|
||||||
getHassioLogDownloadUrl,
|
getHassioLogDownloadUrl,
|
||||||
@ -40,6 +50,7 @@ import { atLeastVersion } from "../../../common/config/version";
|
|||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import { debounce } from "../../../common/util/debounce";
|
import { debounce } from "../../../common/util/debounce";
|
||||||
import { showDownloadLogsDialog } from "./show-dialog-download-logs";
|
import { showDownloadLogsDialog } from "./show-dialog-download-logs";
|
||||||
|
import type { HaMenu } from "../../../components/ha-menu";
|
||||||
|
|
||||||
const NUMBER_OF_LINES = 100;
|
const NUMBER_OF_LINES = 100;
|
||||||
|
|
||||||
@ -64,6 +75,8 @@ class ErrorLogCard extends LitElement {
|
|||||||
|
|
||||||
@query("ha-ansi-to-html") private _ansiToHtmlElement?: HaAnsiToHtml;
|
@query("ha-ansi-to-html") private _ansiToHtmlElement?: HaAnsiToHtml;
|
||||||
|
|
||||||
|
@query("#boots-menu") private _bootsMenu?: HaMenu;
|
||||||
|
|
||||||
@state() private _firstCursor?: string;
|
@state() private _firstCursor?: string;
|
||||||
|
|
||||||
@state() private _scrolledToBottomController =
|
@state() private _scrolledToBottomController =
|
||||||
@ -92,6 +105,10 @@ class ErrorLogCard extends LitElement {
|
|||||||
|
|
||||||
@state() private _numberOfLines?: number;
|
@state() private _numberOfLines?: number;
|
||||||
|
|
||||||
|
@state() private _boot = 0;
|
||||||
|
|
||||||
|
@state() private _boots?: number[];
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="error-log-intro">
|
<div class="error-log-intro">
|
||||||
@ -105,6 +122,60 @@ class ErrorLogCard extends LitElement {
|
|||||||
this.hass.localize("ui.panel.config.logs.show_full_logs")}
|
this.hass.localize("ui.panel.config.logs.show_full_logs")}
|
||||||
</h1>
|
</h1>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
|
${this._streamSupported && Array.isArray(this._boots)
|
||||||
|
? html`
|
||||||
|
<ha-assist-chip
|
||||||
|
.label=${this._boot === 0
|
||||||
|
? this.hass.localize("ui.panel.config.logs.current")
|
||||||
|
: this._boot === -1
|
||||||
|
? this.hass.localize("ui.panel.config.logs.previous")
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.logs.startups_ago",
|
||||||
|
{ boot: this._boot * -1 }
|
||||||
|
)}
|
||||||
|
id="boots-anchor"
|
||||||
|
@click=${this._toggleBootsMenu}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
slot="trailing-icon"
|
||||||
|
.path=${mdiMenuDown}
|
||||||
|
></ha-svg-icon
|
||||||
|
></ha-assist-chip>
|
||||||
|
<ha-menu
|
||||||
|
anchor="boots-anchor"
|
||||||
|
id="boots-menu"
|
||||||
|
positioning="fixed"
|
||||||
|
>
|
||||||
|
${this._boots.map(
|
||||||
|
(boot) => html`
|
||||||
|
<ha-md-menu-item
|
||||||
|
.value=${boot}
|
||||||
|
@click=${this._setBoot}
|
||||||
|
.selected=${boot === this._boot}
|
||||||
|
>
|
||||||
|
${boot === 0
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.logs.current"
|
||||||
|
)
|
||||||
|
: boot === -1
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.logs.previous"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.logs.startups_ago",
|
||||||
|
{ boot: boot * -1 }
|
||||||
|
)}
|
||||||
|
</ha-md-menu-item>
|
||||||
|
${boot === 0
|
||||||
|
? html`<ha-md-divider
|
||||||
|
role="separator"
|
||||||
|
></ha-md-divider>`
|
||||||
|
: nothing}
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-menu>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiDownload}
|
.path=${mdiDownload}
|
||||||
@click=${this._downloadFullLog}
|
@click=${this._downloadFullLog}
|
||||||
@ -212,6 +283,9 @@ class ErrorLogCard extends LitElement {
|
|||||||
if (this.hass?.config.recovery_mode || this.show) {
|
if (this.hass?.config.recovery_mode || this.show) {
|
||||||
this.hass.loadFragmentTranslation("config");
|
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) {
|
||||||
@ -221,6 +295,7 @@ class ErrorLogCard extends LitElement {
|
|||||||
(changedProps.has("show") && this.show) ||
|
(changedProps.has("show") && this.show) ||
|
||||||
(changedProps.has("provider") && this.show)
|
(changedProps.has("provider") && this.show)
|
||||||
) {
|
) {
|
||||||
|
this._boot = 0;
|
||||||
this._loadLogs();
|
this._loadLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +337,7 @@ class ErrorLogCard extends LitElement {
|
|||||||
header: this.header,
|
header: this.header,
|
||||||
provider: this.provider,
|
provider: this.provider,
|
||||||
defaultLineCount: this._numberOfLines,
|
defaultLineCount: this._numberOfLines,
|
||||||
|
boot: this._boot,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const timeString = new Date().toISOString().replace(/:/g, "-");
|
const timeString = new Date().toISOString().replace(/:/g, "-");
|
||||||
@ -306,7 +382,8 @@ class ErrorLogCard extends LitElement {
|
|||||||
this.hass,
|
this.hass,
|
||||||
this.provider,
|
this.provider,
|
||||||
this._logStreamAborter.signal,
|
this._logStreamAborter.signal,
|
||||||
NUMBER_OF_LINES
|
NUMBER_OF_LINES,
|
||||||
|
this._boot
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.headers.has("X-First-Cursor")) {
|
if (response.headers.has("X-First-Cursor")) {
|
||||||
@ -437,7 +514,8 @@ class ErrorLogCard extends LitElement {
|
|||||||
const response = await fetchHassioLogs(
|
const response = await fetchHassioLogs(
|
||||||
this.hass,
|
this.hass,
|
||||||
this.provider,
|
this.provider,
|
||||||
`entries=${this._firstCursor}:-100:100`
|
`entries=${this._firstCursor}:-100:100`,
|
||||||
|
this._boot
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.headers.has("X-First-Cursor")) {
|
if (response.headers.has("X-First-Cursor")) {
|
||||||
@ -488,6 +566,31 @@ class ErrorLogCard extends LitElement {
|
|||||||
return isVisible;
|
return isVisible;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private async _loadBoots() {
|
||||||
|
if (this._streamSupported && isComponentLoaded(this.hass, "hassio")) {
|
||||||
|
try {
|
||||||
|
const { data } = await fetchHassioBoots(this.hass);
|
||||||
|
this._boots = Object.keys(data.boots)
|
||||||
|
.map(Number)
|
||||||
|
.sort((a, b) => b - a);
|
||||||
|
} catch (err: any) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleBootsMenu() {
|
||||||
|
if (this._bootsMenu) {
|
||||||
|
this._bootsMenu.open = !this._bootsMenu.open;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setBoot(ev: any) {
|
||||||
|
this._boot = ev.target.value;
|
||||||
|
this._loadLogs();
|
||||||
|
}
|
||||||
|
|
||||||
static styles: CSSResultGroup = css`
|
static styles: CSSResultGroup = css`
|
||||||
.error-log-intro {
|
.error-log-intro {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -600,6 +703,11 @@ class ErrorLogCard extends LitElement {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-assist-chip {
|
||||||
|
--ha-assist-chip-container-shape: 10px;
|
||||||
|
--md-assist-chip-trailing-space: 8px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ export interface DownloadLogsDialogParams {
|
|||||||
header?: string;
|
header?: string;
|
||||||
provider: string;
|
provider: string;
|
||||||
defaultLineCount?: number;
|
defaultLineCount?: number;
|
||||||
|
boot: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const showDownloadLogsDialog = (
|
export const showDownloadLogsDialog = (
|
||||||
|
@ -2490,6 +2490,9 @@
|
|||||||
"scroll_down_button": "New logs - Click to scroll",
|
"scroll_down_button": "New logs - Click to scroll",
|
||||||
"provider_not_found": "Log provider not found",
|
"provider_not_found": "Log provider not found",
|
||||||
"provider_not_available": "Logs for ''{provider}'' are not available on your system.",
|
"provider_not_available": "Logs for ''{provider}'' are not available on your system.",
|
||||||
|
"current": "Current",
|
||||||
|
"previous": "Previous",
|
||||||
|
"startups_ago": "{boot} startups ago",
|
||||||
"detail": {
|
"detail": {
|
||||||
"logger": "Logger",
|
"logger": "Logger",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user