diff --git a/src/data/hassio/supervisor.ts b/src/data/hassio/supervisor.ts index 8bf9d715703d..e16d3220be40 100644 --- a/src/data/hassio/supervisor.ts +++ b/src/data/hassio/supervisor.ts @@ -65,6 +65,10 @@ export type HassioInfo = { timezone: string; }; +export type HassioBoots = { + boots: Record; +}; + export type HassioPanelInfo = PanelInfo< | undefined | { @@ -177,14 +181,18 @@ export const fetchHassioInfo = async ( ); }; +export const fetchHassioBoots = async (hass: HomeAssistant) => + hass.callApi>("GET", `hassio/host/logs/boots`); + export const fetchHassioLogs = async ( hass: HomeAssistant, provider: string, - range?: string + range?: string, + boot = 0 ) => hass.callApiRaw( "GET", - `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs`, + `hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs/boots/${boot}`, undefined, range ? { @@ -197,11 +205,12 @@ export const fetchHassioLogsFollow = async ( hass: HomeAssistant, provider: string, signal: AbortSignal, - lines = 100 + lines = 100, + boot = 0 ) => hass.callApiRaw( "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, signal @@ -212,10 +221,14 @@ export const getHassioLogDownloadUrl = (provider: string) => provider.includes("_") ? `addons/${provider}` : provider }/logs`; -export const getHassioLogDownloadLinesUrl = (provider: string, lines: number) => +export const getHassioLogDownloadLinesUrl = ( + provider: string, + lines: number, + boot = 0 +) => `/api/hassio/${ provider.includes("_") ? `addons/${provider}` : provider - }/logs?lines=${lines}`; + }/logs/boots/${boot}?lines=${lines}`; export const setSupervisorOption = async ( hass: HomeAssistant, diff --git a/src/panels/config/logs/dialog-download-logs.ts b/src/panels/config/logs/dialog-download-logs.ts index cd711fa5a2b4..fdb5e7242ea4 100644 --- a/src/panels/config/logs/dialog-download-logs.ts +++ b/src/panels/config/logs/dialog-download-logs.ts @@ -65,7 +65,11 @@ class DownloadLogsDialog extends LitElement { ${this.hass.localize("ui.panel.config.logs.download_full_log")} - ${this._dialogParams.header} + + ${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 })}`} +
@@ -104,9 +108,14 @@ class DownloadLogsDialog extends LitElement { private async _dowloadLogs() { const provider = this._dialogParams!.provider; + const boot = this._dialogParams!.boot; const timeString = new Date().toISOString().replace(/:/g, "-"); - const downloadUrl = getHassioLogDownloadLinesUrl(provider, this._lineCount); + const downloadUrl = getHassioLogDownloadLinesUrl( + provider, + this._lineCount, + boot + ); const logFileName = provider !== "core" ? `${provider}_${timeString}.log` diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts index 41bd9eea19f6..ae17a7a16466 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -1,5 +1,10 @@ import "@material/mwc-list/mwc-list-item"; -import { mdiArrowCollapseDown, mdiDownload, mdiRefresh } from "@mdi/js"; +import { + mdiArrowCollapseDown, + mdiDownload, + mdiMenuDown, + mdiRefresh, +} from "@mdi/js"; import { css, CSSResultGroup, @@ -22,12 +27,17 @@ import "../../../components/ha-button"; import "../../../components/ha-icon-button"; import "../../../components/ha-svg-icon"; 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 { fetchErrorLog, getErrorLogDownloadUrl } from "../../../data/error_log"; import { extractApiErrorMessage } from "../../../data/hassio/common"; import { + fetchHassioBoots, fetchHassioLogs, fetchHassioLogsFollow, getHassioLogDownloadUrl, @@ -40,6 +50,7 @@ import { atLeastVersion } from "../../../common/config/version"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { debounce } from "../../../common/util/debounce"; import { showDownloadLogsDialog } from "./show-dialog-download-logs"; +import type { HaMenu } from "../../../components/ha-menu"; const NUMBER_OF_LINES = 100; @@ -64,6 +75,8 @@ class ErrorLogCard extends LitElement { @query("ha-ansi-to-html") private _ansiToHtmlElement?: HaAnsiToHtml; + @query("#boots-menu") private _bootsMenu?: HaMenu; + @state() private _firstCursor?: string; @state() private _scrolledToBottomController = @@ -92,6 +105,10 @@ class ErrorLogCard extends LitElement { @state() private _numberOfLines?: number; + @state() private _boot = 0; + + @state() private _boots?: number[]; + protected render(): TemplateResult { return html`
@@ -105,6 +122,60 @@ class ErrorLogCard extends LitElement { this.hass.localize("ui.panel.config.logs.show_full_logs")}
+ ${this._streamSupported && Array.isArray(this._boots) + ? html` + + + + ${this._boots.map( + (boot) => html` + + ${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 } + )} + + ${boot === 0 + ? html`` + : nothing} + ` + )} + + ` + : nothing} 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` .error-log-intro { text-align: center; @@ -600,6 +703,11 @@ class ErrorLogCard extends LitElement { justify-content: center; padding: 16px; } + + ha-assist-chip { + --ha-assist-chip-container-shape: 10px; + --md-assist-chip-trailing-space: 8px; + } `; } diff --git a/src/panels/config/logs/show-dialog-download-logs.ts b/src/panels/config/logs/show-dialog-download-logs.ts index e61d0c22ad7e..e0267075fe4c 100644 --- a/src/panels/config/logs/show-dialog-download-logs.ts +++ b/src/panels/config/logs/show-dialog-download-logs.ts @@ -4,6 +4,7 @@ export interface DownloadLogsDialogParams { header?: string; provider: string; defaultLineCount?: number; + boot: number; } export const showDownloadLogsDialog = ( diff --git a/src/translations/en.json b/src/translations/en.json index ce5d7ee5e829..69e0397c6622 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2490,6 +2490,9 @@ "scroll_down_button": "New logs - Click to scroll", "provider_not_found": "Log provider not found", "provider_not_available": "Logs for ''{provider}'' are not available on your system.", + "current": "Current", + "previous": "Previous", + "startups_ago": "{boot} startups ago", "detail": { "logger": "Logger", "source": "Source",