diff --git a/hassio/src/addon-view/log/hassio-addon-log-tab.ts b/hassio/src/addon-view/log/hassio-addon-log-tab.ts index 9f52d3e83296..a40454fe8367 100644 --- a/hassio/src/addon-view/log/hassio-addon-log-tab.ts +++ b/hassio/src/addon-view/log/hassio-addon-log-tab.ts @@ -38,12 +38,13 @@ class HassioAddonLogDashboard extends LitElement { @value-changed=${this._filterChanged} .hass=${this.hass} .filter=${this._filter} - .label=${this.hass.localize("ui.panel.config.logs.search")} + .label=${this.supervisor.localize("ui.panel.config.logs.search")} >
`; + return html`
`;
   }
 
   protected firstUpdated(_changedProperties: PropertyValues): void {
@@ -47,9 +51,11 @@ export class HaAnsiToHtml extends LitElement {
     return css`
       pre {
         overflow-x: auto;
+        margin: 0;
+      }
+      pre.wrap {
         white-space: pre-wrap;
         overflow-wrap: break-word;
-        margin: 0;
       }
       .bold {
         font-weight: bold;
diff --git a/src/components/trace/ha-trace-logbook.ts b/src/components/trace/ha-trace-logbook.ts
index b0007d28479f..08158cb2bc31 100644
--- a/src/components/trace/ha-trace-logbook.ts
+++ b/src/components/trace/ha-trace-logbook.ts
@@ -26,7 +26,10 @@ export class HaTraceLogbook extends LitElement {
             .entries=${this.logbookEntries}
             .narrow=${this.narrow}
           >
-          
+          
         `
       : html`
No Logbook entries found for this step. diff --git a/src/components/trace/ha-trace-path-details.ts b/src/components/trace/ha-trace-path-details.ts index 2a38ca6504c8..410f05bd7bf0 100644 --- a/src/components/trace/ha-trace-path-details.ts +++ b/src/components/trace/ha-trace-path-details.ts @@ -291,7 +291,10 @@ export class HaTracePathDetails extends LitElement { .entries=${entries} .narrow=${this.narrow} > - + ` : html`
${this.hass!.localize( diff --git a/src/components/trace/ha-trace-timeline.ts b/src/components/trace/ha-trace-timeline.ts index 7c62b3fbb033..07e7fc360026 100644 --- a/src/components/trace/ha-trace-timeline.ts +++ b/src/components/trace/ha-trace-timeline.ts @@ -28,7 +28,10 @@ export class HaTraceTimeline extends LitElement { allowPick > - + `; } diff --git a/src/components/trace/hat-logbook-note.ts b/src/components/trace/hat-logbook-note.ts index 1d790a21afa7..bda976fe28af 100644 --- a/src/components/trace/hat-logbook-note.ts +++ b/src/components/trace/hat-logbook-note.ts @@ -1,14 +1,22 @@ -import { css, html, LitElement } from "lit"; +import { css, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; +import type { HomeAssistant } from "../../types"; @customElement("hat-logbook-note") class HatLogbookNote extends LitElement { - @property() public domain = "automation"; + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public domain: "automation" | "script" = "automation"; render() { - return html` - Not all shown logbook entries might be related to this ${this.domain}. - `; + if (this.domain === "script") { + return this.hass.localize( + "ui.panel.config.automation.trace.messages.not_all_entries_are_related_script_note" + ); + } + return this.hass.localize( + "ui.panel.config.automation.trace.messages.not_all_entries_are_related_automation_note" + ); } static styles = css` diff --git a/src/dialogs/more-info/controls/more-info-update.ts b/src/dialogs/more-info/controls/more-info-update.ts index 7014a505e99f..5fe6d6d792c6 100644 --- a/src/dialogs/more-info/controls/more-info-update.ts +++ b/src/dialogs/more-info/controls/more-info-update.ts @@ -52,59 +52,61 @@ class MoreInfoUpdate extends LitElement { return html`
- ${this.stateObj.attributes.in_progress - ? supportsFeature(this.stateObj, UpdateEntityFeature.PROGRESS) && - this.stateObj.attributes.update_percentage !== null - ? html`` - : html`` - : nothing} -

${this.stateObj.attributes.title}

- ${this._error - ? html`${this._error}` - : nothing} -
-
- ${this.hass.formatEntityAttributeName( - this.stateObj, - "installed_version" - )} -
-
- ${this.stateObj.attributes.installed_version ?? - this.hass.localize("state.default.unavailable")} -
-
-
-
- ${this.hass.formatEntityAttributeName( - this.stateObj, - "latest_version" - )} +
+ ${this.stateObj.attributes.in_progress + ? supportsFeature(this.stateObj, UpdateEntityFeature.PROGRESS) && + this.stateObj.attributes.update_percentage !== null + ? html`` + : html`` + : nothing} +

${this.stateObj.attributes.title}

+ ${this._error + ? html`${this._error}` + : nothing} +
+
+ ${this.hass.formatEntityAttributeName( + this.stateObj, + "installed_version" + )} +
+
+ ${this.stateObj.attributes.installed_version ?? + this.hass.localize("state.default.unavailable")} +
-
- ${this.stateObj.attributes.latest_version ?? - this.hass.localize("state.default.unavailable")} +
+
+ ${this.hass.formatEntityAttributeName( + this.stateObj, + "latest_version" + )} +
+
+ ${this.stateObj.attributes.latest_version ?? + this.hass.localize("state.default.unavailable")} +
-
- ${this.stateObj.attributes.release_url - ? html`` - : nothing} + ${this.stateObj.attributes.release_url + ? html`` + : nothing} +
${supportsFeature(this.stateObj!, UpdateEntityFeature.RELEASE_NOTES) && !this._error ? this._releaseNotes === undefined @@ -293,6 +295,11 @@ class MoreInfoUpdate extends LitElement { ha-expansion-panel { margin: 16px 0; } + + .summary { + margin-bottom: 16px; + } + .row { margin: 0; display: flex; @@ -308,7 +315,9 @@ class MoreInfoUpdate extends LitElement { ); position: sticky; bottom: 0; - margin: 0 -24px -24px -24px; + margin: 0 -24px 0 -24px; + margin-bottom: calc(-1 * max(env(safe-area-inset-bottom), 24px)); + padding-bottom: env(safe-area-inset-bottom); box-sizing: border-box; display: flex; flex-direction: column; diff --git a/src/external_app/external_messaging.ts b/src/external_app/external_messaging.ts index a870b72be2ff..292746bdee17 100644 --- a/src/external_app/external_messaging.ts +++ b/src/external_app/external_messaging.ts @@ -264,6 +264,7 @@ export interface ExternalConfig { hasAssist: boolean; hasBarCodeScanner: number; canSetupImprov: boolean; + downloadFileSupported: boolean; } export class ExternalMessaging { diff --git a/src/panels/config/automation/add-automation-element-dialog.ts b/src/panels/config/automation/add-automation-element-dialog.ts index 99e497a70b6f..c5d298e44db8 100644 --- a/src/panels/config/automation/add-automation-element-dialog.ts +++ b/src/panels/config/automation/add-automation-element-dialog.ts @@ -370,7 +370,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { }`, description: this.hass.localize( - `component.${domain}.services.${service}.description` + `component.${dmn}.services.${service}.description` ) || services[dmn][service]?.description, }); } diff --git a/src/panels/config/logs/dialog-download-logs.ts b/src/panels/config/logs/dialog-download-logs.ts index 0a7e9d965f40..801ef1db2262 100644 --- a/src/panels/config/logs/dialog-download-logs.ts +++ b/src/panels/config/logs/dialog-download-logs.ts @@ -17,19 +17,21 @@ import type { HomeAssistant } from "../../../types"; import { fileDownload } from "../../../util/file_download"; import type { DownloadLogsDialogParams } from "./show-dialog-download-logs"; +const DEFAULT_LINE_COUNT = 500; + @customElement("dialog-download-logs") class DownloadLogsDialog extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @state() private _dialogParams?: DownloadLogsDialogParams; - @state() private _lineCount = 100; + @state() private _lineCount = DEFAULT_LINE_COUNT; @query("ha-md-dialog") private _dialogElement!: HaMdDialog; public showDialog(dialogParams: DownloadLogsDialogParams) { this._dialogParams = dialogParams; - this._lineCount = this._dialogParams?.defaultLineCount ?? 100; + this._lineCount = this._dialogParams?.defaultLineCount || 500; } public closeDialog() { @@ -38,7 +40,7 @@ class DownloadLogsDialog extends LitElement { private _dialogClosed() { this._dialogParams = undefined; - this._lineCount = 100; + this._lineCount = DEFAULT_LINE_COUNT; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -48,7 +50,7 @@ class DownloadLogsDialog extends LitElement { } const numberOfLinesOptions = [100, 500, 1000, 5000, 10000]; - if (!numberOfLinesOptions.includes(this._lineCount)) { + if (!numberOfLinesOptions.includes(this._lineCount) && this._lineCount) { numberOfLinesOptions.push(this._lineCount); numberOfLinesOptions.sort((a, b) => a - b); } @@ -63,7 +65,7 @@ class DownloadLogsDialog extends LitElement { .path=${mdiClose} > - ${this.hass.localize("ui.panel.config.logs.download_full_log")} + ${this.hass.localize("ui.panel.config.logs.download_logs")} ${this._dialogParams.header}${this._dialogParams.boot === 0 @@ -95,7 +97,7 @@ class DownloadLogsDialog extends LitElement { ${this.hass.localize("ui.common.cancel")} - + ${this.hass.localize("ui.common.download")}
@@ -103,7 +105,7 @@ class DownloadLogsDialog extends LitElement { `; } - private async _dowloadLogs() { + private async _downloadLogs() { const provider = this._dialogParams!.provider; const boot = this._dialogParams!.boot; diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts index 259a87ca7d12..9bd729632bde 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -1,9 +1,16 @@ import "@material/mwc-list/mwc-list-item"; +import type { ActionDetail } from "@material/mwc-list"; + import { mdiArrowCollapseDown, + mdiDotsVertical, + mdiCircle, mdiDownload, + mdiFormatListNumbered, mdiMenuDown, mdiRefresh, + mdiWrap, + mdiWrapDisabled, } from "@mdi/js"; import { css, @@ -31,6 +38,8 @@ import "../../../components/chips/ha-assist-chip"; import "../../../components/ha-menu"; import "../../../components/ha-md-menu-item"; import "../../../components/ha-md-divider"; +import "../../../components/ha-button-menu"; +import "../../../components/ha-list-item"; import { getSignedPath } from "../../../data/auth"; @@ -40,10 +49,14 @@ import { fetchHassioBoots, fetchHassioLogs, fetchHassioLogsFollow, + getHassioLogDownloadLinesUrl, getHassioLogDownloadUrl, } from "../../../data/hassio/supervisor"; import type { HomeAssistant } from "../../../types"; -import { fileDownload } from "../../../util/file_download"; +import { + downloadFileSupported, + fileDownload, +} from "../../../util/file_download"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import type { ConnectionStatus } from "../../../data/connection-status"; import { atLeastVersion } from "../../../common/config/version"; @@ -51,6 +64,7 @@ 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"; +import type { LocalizeFunc } from "../../../common/translations/localize"; const NUMBER_OF_LINES = 100; @@ -58,6 +72,8 @@ const NUMBER_OF_LINES = 100; class ErrorLogCard extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public localizeFunc?: LocalizeFunc; + @property() public filter = ""; @property() public header?: string; @@ -109,30 +125,42 @@ class ErrorLogCard extends LitElement { @state() private _boots?: number[]; + @state() private _showBootsSelect = false; + + @state() private _wrapLines = true; + + @state() private _downloadSupported; + + @state() private _logsFileLink; + protected render(): TemplateResult { + const localize = this.localizeFunc || this.hass.localize; return html`
${this._error ? html`${this._error}` - : ""} + : nothing}

- ${this.header || - this.hass.localize("ui.panel.config.logs.show_full_logs")} + ${this.header || localize("ui.panel.config.logs.show_full_logs")}

- ${this._streamSupported && Array.isArray(this._boots) + ${this._streamSupported && + Array.isArray(this._boots) && + this._showBootsSelect ? html` @@ -154,14 +182,10 @@ class ErrorLogCard extends LitElement { .selected=${boot === this._boot} > ${boot === 0 - ? this.hass.localize( - "ui.panel.config.logs.current" - ) + ? localize("ui.panel.config.logs.current") : boot === -1 - ? this.hass.localize( - "ui.panel.config.logs.previous" - ) - : this.hass.localize( + ? localize("ui.panel.config.logs.previous") + : localize( "ui.panel.config.logs.startups_ago", { boot: boot * -1 } )} @@ -176,20 +200,61 @@ class ErrorLogCard extends LitElement { ` : nothing} + ${this._downloadSupported + ? html` + + ` + : this._logsFileLink + ? html` + + + + ` + : nothing} ${!this._streamSupported || this._error ? html`` : nothing} + ${this._streamSupported && Array.isArray(this._boots) + ? html` + + + + + + ${localize( + `ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots` + )} + + + ` + : nothing}
@@ -202,25 +267,22 @@ class ErrorLogCard extends LitElement {
` : nothing} ${this._loadingState === "loading" - ? html`
- ${this.hass.localize("ui.panel.config.logs.loading_log")} -
` + ? html`
${localize("ui.panel.config.logs.loading_log")}
` : this._loadingState === "empty" - ? html`
- ${this.hass.localize("ui.panel.config.logs.no_errors")} -
` + ? html`
${localize("ui.panel.config.logs.no_errors")}
` : nothing} ${this._loadingState === "loaded" && this.filter && this._noSearchResults ? html`
- ${this.hass.localize( - "ui.panel.config.logs.no_issues_search", - { term: this.filter } - )} + ${localize("ui.panel.config.logs.no_issues_search", { + term: this.filter, + })}
` : nothing} - +
- ${this.hass.localize("ui.panel.config.logs.scroll_down_button")} + ${localize("ui.panel.config.logs.scroll_down_button")} + ${this._streamSupported && + this._loadingState !== "loading" && + !this._error + ? html`
+ + Live +
` + : nothing} ${this.show === false ? html` - - - ${this.hass.localize("ui.panel.config.logs.download_full_log")} - + ${this._downloadSupported + ? html` + + + ${localize("ui.panel.config.logs.download_logs")} + + ` + : nothing} - ${this.hass.localize("ui.panel.config.logs.load_logs")} + ${localize("ui.panel.config.logs.load_logs")} ` - : ""} + : nothing}
`; } @@ -268,6 +342,9 @@ class ErrorLogCard extends LitElement { 11 ); } + if (this._downloadSupported === undefined && this.hass) { + this._downloadSupported = downloadFileSupported(this.hass); + } } protected firstUpdated(changedProps: PropertyValues) { @@ -331,7 +408,7 @@ class ErrorLogCard extends LitElement { ); } - private async _downloadFullLog(): Promise { + private async _downloadLogs(): Promise { if (this._streamSupported) { showDownloadLogsDialog(this, { header: this.header, @@ -378,6 +455,18 @@ class ErrorLogCard extends LitElement { isComponentLoaded(this.hass, "hassio") && this.provider ) { + // check if there are any logs at all + const testResponse = await fetchHassioLogs( + this.hass, + this.provider, + `entries=:-1:`, + this._boot + ); + const testLogs = await testResponse.text(); + if (!testLogs.trim()) { + this._loadingState = "empty"; + } + const response = await fetchHassioLogsFollow( this.hass, this.provider, @@ -438,6 +527,17 @@ class ErrorLogCard extends LitElement { } else { this._newLogsIndicator = true; } + + if (!this._downloadSupported) { + const downloadUrl = getHassioLogDownloadLinesUrl( + this.provider, + this._numberOfLines, + this._boot + ); + getSignedPath(this.hass, downloadUrl).then((signedUrl) => { + this._logsFileLink = signedUrl.path; + }); + } } } } else { @@ -461,10 +561,13 @@ class ErrorLogCard extends LitElement { if (err.name === "AbortError") { return; } - this._error = this.hass.localize("ui.panel.config.logs.failed_get_logs", { - provider: this.provider, - error: extractApiErrorMessage(err), - }); + this._error = (this.localizeFunc || this.hass.localize)( + "ui.panel.config.logs.failed_get_logs", + { + provider: this.provider, + error: extractApiErrorMessage(err), + } + ); } } @@ -585,6 +688,18 @@ class ErrorLogCard extends LitElement { } } + private _toggleLineWrap() { + this._wrapLines = !this._wrapLines; + } + + private _handleOverflowAction(ev: CustomEvent) { + switch (ev.detail.index) { + case 0: + this._showBootsSelect = !this._showBootsSelect; + break; + } + } + private _toggleBootsMenu() { if (this._bootsMenu) { this._bootsMenu.open = !this._bootsMenu.open; @@ -597,6 +712,9 @@ class ErrorLogCard extends LitElement { } static styles: CSSResultGroup = css` + :host { + direction: var(--direction); + } .error-log-intro { text-align: center; margin: 16px; @@ -646,7 +764,7 @@ class ErrorLogCard extends LitElement { position: relative; font-family: var(--code-font-family, monospace); clear: both; - text-align: left; + text-align: start; padding-top: 12px; padding-bottom: 12px; overflow-y: scroll; @@ -713,6 +831,36 @@ class ErrorLogCard extends LitElement { --ha-assist-chip-container-shape: 10px; --md-assist-chip-trailing-space: 8px; } + + @keyframes breathe { + from { + opacity: 0.8; + } + to { + opacity: 0; + } + } + + .live-indicator { + position: absolute; + bottom: 0; + inset-inline-end: 16px; + border-top-right-radius: 8px; + border-top-left-radius: 8px; + background-color: var(--primary-color); + color: var(--text-primary-color); + padding: 4px 8px; + opacity: 0.8; + } + .live-indicator ha-svg-icon { + animation: breathe 1s cubic-bezier(0.5, 0, 1, 1) infinite alternate; + height: 14px; + width: 14px; + } + + .download-link { + color: var(--text-color); + } `; } diff --git a/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts b/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts index 8db4e6b44a6b..5e021d53dad3 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-date-selection-card.ts @@ -1,11 +1,11 @@ import type { CSSResultGroup, PropertyValues } from "lit"; import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import "../../../../components/ha-card"; import type { HomeAssistant } from "../../../../types"; import { hasConfigChanged } from "../../common/has-changed"; import "../../components/hui-energy-period-selector"; -import "../../../../components/ha-card"; -import type { LovelaceCard, LovelaceLayoutOptions } from "../../types"; +import type { LovelaceCard, LovelaceGridOptions } from "../../types"; import type { EnergyCardBaseConfig } from "../types"; @customElement("hui-energy-date-selection-card") @@ -21,10 +21,10 @@ export class HuiEnergyDateSelectionCard return 1; } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { return { - grid_rows: 1, - grid_columns: 4, + rows: 1, + columns: 12, }; } diff --git a/src/panels/lovelace/cards/hui-area-card.ts b/src/panels/lovelace/cards/hui-area-card.ts index 1e2b335ade56..e8c0eb8cea78 100644 --- a/src/panels/lovelace/cards/hui-area-card.ts +++ b/src/panels/lovelace/cards/hui-area-card.ts @@ -45,7 +45,7 @@ import "../components/hui-warning"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { AreaCardConfig } from "./types"; @@ -534,10 +534,11 @@ export class HuiAreaCard forwardHaptic("light"); } - getLayoutOptions(): LovelaceLayoutOptions { + getGridOptions(): LovelaceGridOptions { return { - grid_columns: 4, - grid_rows: 3, + columns: 12, + rows: 3, + min_columns: 3, }; } diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 2bed7c6038e9..2fbb0adb2f9e 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -46,7 +46,7 @@ import { createEntityNotFoundWarning } from "../components/hui-warning"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { ButtonCardConfig } from "./types"; @@ -134,20 +134,23 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { ); } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { if ( this._config?.show_icon && (this._config?.show_name || this._config?.show_state) ) { return { - grid_rows: 2, - grid_columns: 2, - grid_min_rows: 2, + rows: 2, + columns: 6, + min_columns: 2, + min_rows: 2, }; } return { - grid_rows: 1, - grid_columns: 1, + rows: 1, + columns: 3, + min_columns: 2, + min_rows: 1, }; } diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index b2b849525540..6e5b30e2b44d 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -33,8 +33,8 @@ import { createEntityNotFoundWarning } from "../components/hui-warning"; import { createHeaderFooterElement } from "../create-element/create-header-footer-element"; import type { LovelaceCard, + LovelaceGridOptions, LovelaceHeaderFooter, - LovelaceLayoutOptions, } from "../types"; import type { HuiErrorCard } from "./hui-error-card"; import type { EntityCardConfig } from "./types"; @@ -249,12 +249,12 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { fireEvent(this, "hass-more-info", { entityId: this._config!.entity }); } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { return { - grid_columns: 2, - grid_rows: 2, - grid_min_columns: 2, - grid_min_rows: 2, + columns: 6, + rows: 2, + min_columns: 6, + min_rows: 2, }; } diff --git a/src/panels/lovelace/cards/hui-heading-card.ts b/src/panels/lovelace/cards/hui-heading-card.ts index 6d57a1ae064c..307fc6bfdbe2 100644 --- a/src/panels/lovelace/cards/hui-heading-card.ts +++ b/src/panels/lovelace/cards/hui-heading-card.ts @@ -16,7 +16,7 @@ import "../heading-badges/hui-heading-badge"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { HeadingCardConfig } from "./types"; @@ -65,10 +65,11 @@ export class HuiHeadingCard extends LitElement implements LovelaceCard { return 1; } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { return { - grid_columns: "full", - grid_rows: this._config?.heading_style === "subtitle" ? "auto" : 1, + columns: "full", + rows: this._config?.heading_style === "subtitle" ? "auto" : 1, + min_columns: 3, }; } diff --git a/src/panels/lovelace/cards/hui-humidifier-card.ts b/src/panels/lovelace/cards/hui-humidifier-card.ts index c342934afc6e..15bee2a43d8f 100644 --- a/src/panels/lovelace/cards/hui-humidifier-card.ts +++ b/src/panels/lovelace/cards/hui-humidifier-card.ts @@ -19,7 +19,7 @@ import { createEntityNotFoundWarning } from "../components/hui-warning"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { HumidifierCardConfig } from "./types"; @@ -171,21 +171,21 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard { `; } - public getLayoutOptions(): LovelaceLayoutOptions { - const grid_columns = 4; - let grid_rows = 5; - let grid_min_rows = 2; - const grid_min_columns = 2; + public getGridOptions(): LovelaceGridOptions { + const columns = 12; + let rows = 5; + let min_rows = 2; + const min_columns = 6; if (this._config?.features?.length) { const featureHeight = Math.ceil((this._config.features.length * 2) / 3); - grid_rows += featureHeight; - grid_min_rows += featureHeight; + rows += featureHeight; + min_rows += featureHeight; } return { - grid_columns, - grid_rows, - grid_min_rows, - grid_min_columns, + columns, + rows, + min_columns, + min_rows, }; } diff --git a/src/panels/lovelace/cards/hui-iframe-card.ts b/src/panels/lovelace/cards/hui-iframe-card.ts index aa078608cebc..72188af3ba7d 100644 --- a/src/panels/lovelace/cards/hui-iframe-card.ts +++ b/src/panels/lovelace/cards/hui-iframe-card.ts @@ -11,7 +11,7 @@ import { IFRAME_SANDBOX } from "../../../util/iframe"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { IframeCardConfig } from "./types"; @@ -113,11 +113,12 @@ export class HuiIframeCard extends LitElement implements LovelaceCard { `; } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { return { - grid_columns: "full", - grid_rows: 4, - grid_min_rows: 2, + columns: "full", + rows: 4, + min_columns: 3, + min_rows: 2, }; } diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts index 9931303f93f6..f1234225722c 100644 --- a/src/panels/lovelace/cards/hui-map-card.ts +++ b/src/panels/lovelace/cards/hui-map-card.ts @@ -11,8 +11,8 @@ import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { deepEqual } from "../../../common/util/deep-equal"; import parseAspectRatio from "../../../common/util/parse-aspect-ratio"; -import "../../../components/ha-card"; import "../../../components/ha-alert"; +import "../../../components/ha-card"; import "../../../components/ha-icon-button"; import "../../../components/map/ha-map"; import type { @@ -23,15 +23,15 @@ import type { } from "../../../components/map/ha-map"; import type { HistoryStates } from "../../../data/history"; import { subscribeHistoryStatesTimeWindow } from "../../../data/history"; +import type { HomeAssistant } from "../../../types"; +import { findEntities } from "../common/find-entities"; import { hasConfigChanged, hasConfigOrEntitiesChanged, } from "../common/has-changed"; -import type { HomeAssistant } from "../../../types"; -import { findEntities } from "../common/find-entities"; import { processConfigEntities } from "../common/process-config-entities"; import type { EntityConfig } from "../entity-rows/types"; -import type { LovelaceCard, LovelaceLayoutOptions } from "../types"; +import type { LovelaceCard, LovelaceGridOptions } from "../types"; import type { MapCardConfig } from "./types"; export const DEFAULT_HOURS_TO_SHOW = 0; @@ -431,12 +431,12 @@ class HuiMapCard extends LitElement implements LovelaceCard { } ); - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { return { - grid_columns: "full", - grid_rows: 4, - grid_min_columns: 2, - grid_min_rows: 2, + columns: "full", + rows: 4, + min_columns: 6, + min_rows: 2, }; } diff --git a/src/panels/lovelace/cards/hui-sensor-card.ts b/src/panels/lovelace/cards/hui-sensor-card.ts index 0bc454e3c533..db964f9e0d6b 100644 --- a/src/panels/lovelace/cards/hui-sensor-card.ts +++ b/src/panels/lovelace/cards/hui-sensor-card.ts @@ -6,7 +6,7 @@ import { computeDomain } from "../../../common/entity/compute_domain"; import type { HomeAssistant } from "../../../types"; import { findEntities } from "../common/find-entities"; import type { GraphHeaderFooterConfig } from "../header-footer/types"; -import type { LovelaceCardEditor, LovelaceLayoutOptions } from "../types"; +import type { LovelaceCardEditor, LovelaceGridOptions } from "../types"; import { HuiEntityCard } from "./hui-entity-card"; import type { EntityCardConfig, SensorCardConfig } from "./types"; @@ -73,12 +73,12 @@ class HuiSensorCard extends HuiEntityCard { super.setConfig(entityCardConfig); } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { return { - grid_columns: 2, - grid_rows: 2, - grid_min_columns: 2, - grid_min_rows: 2, + columns: 6, + rows: 2, + min_columns: 6, + min_rows: 2, }; } diff --git a/src/panels/lovelace/cards/hui-statistic-card.ts b/src/panels/lovelace/cards/hui-statistic-card.ts index e0ee0c079e17..7fafcf8a7db3 100644 --- a/src/panels/lovelace/cards/hui-statistic-card.ts +++ b/src/panels/lovelace/cards/hui-statistic-card.ts @@ -26,7 +26,7 @@ import type { LovelaceCard, LovelaceCardEditor, LovelaceHeaderFooter, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { HuiErrorCard } from "./hui-error-card"; import type { EntityCardConfig, StatisticCardConfig } from "./types"; @@ -249,12 +249,12 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard { fireEvent(this, "hass-more-info", { entityId: this._config!.entity }); } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { return { - grid_columns: 2, - grid_rows: 2, - grid_min_columns: 2, - grid_min_rows: 2, + columns: 6, + rows: 2, + min_columns: 6, + min_rows: 2, }; } diff --git a/src/panels/lovelace/cards/hui-thermostat-card.ts b/src/panels/lovelace/cards/hui-thermostat-card.ts index f96264e75eea..f33d1dbdfe50 100644 --- a/src/panels/lovelace/cards/hui-thermostat-card.ts +++ b/src/panels/lovelace/cards/hui-thermostat-card.ts @@ -19,7 +19,7 @@ import { createEntityNotFoundWarning } from "../components/hui-warning"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { ThermostatCardConfig } from "./types"; @@ -163,21 +163,21 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard { `; } - public getLayoutOptions(): LovelaceLayoutOptions { - const grid_columns = 4; - let grid_rows = 5; - let grid_min_rows = 2; - const grid_min_columns = 2; + public getGridOptions(): LovelaceGridOptions { + const columns = 12; + let rows = 5; + let min_rows = 2; + const min_columns = 6; if (this._config?.features?.length) { const featureHeight = Math.ceil((this._config.features.length * 2) / 3); - grid_rows += featureHeight; - grid_min_rows += featureHeight; + rows += featureHeight; + min_rows += featureHeight; } return { - grid_columns, - grid_rows, - grid_min_rows, - grid_min_columns, + columns, + rows, + min_columns, + min_rows, }; } diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 820926d4bb16..457363681fd6 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -34,7 +34,7 @@ import { hasAction } from "../common/has-action"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import { renderTileBadge } from "./tile/badges/tile-badge"; import type { ThermostatCardConfig, TileCardConfig } from "./types"; @@ -109,22 +109,22 @@ export class HuiTileCard extends LitElement implements LovelaceCard { ); } - public getLayoutOptions(): LovelaceLayoutOptions { - const grid_columns = 2; - let grid_min_columns = 2; - let grid_rows = 1; + public getGridOptions(): LovelaceGridOptions { + const columns = 6; + let min_columns = 6; + let rows = 1; if (this._config?.features?.length) { - grid_rows += this._config.features.length; + rows += this._config.features.length; } if (this._config?.vertical) { - grid_rows++; - grid_min_columns = 1; + rows++; + min_columns = 3; } return { - grid_columns, - grid_rows, - grid_min_rows: grid_rows, - grid_min_columns, + columns, + rows, + min_columns, + min_rows: rows, }; } diff --git a/src/panels/lovelace/cards/hui-weather-forecast-card.ts b/src/panels/lovelace/cards/hui-weather-forecast-card.ts index 5fc2ec050438..ca8fc571af5e 100644 --- a/src/panels/lovelace/cards/hui-weather-forecast-card.ts +++ b/src/panels/lovelace/cards/hui-weather-forecast-card.ts @@ -34,7 +34,7 @@ import { createEntityNotFoundWarning } from "../components/hui-warning"; import type { LovelaceCard, LovelaceCardEditor, - LovelaceLayoutOptions, + LovelaceGridOptions, } from "../types"; import type { WeatherForecastCardConfig } from "./types"; @@ -418,31 +418,31 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard { return typeof item !== "undefined" && item !== null; } - public getLayoutOptions(): LovelaceLayoutOptions { + public getGridOptions(): LovelaceGridOptions { if ( this._config?.show_current !== false && this._config?.show_forecast !== false ) { return { - grid_columns: 4, - grid_min_columns: 2, - grid_rows: 4, - grid_min_rows: 4, + columns: 12, + rows: 4, + min_columns: 6, + min_rows: 4, }; } if (this._config?.show_forecast !== false) { return { - grid_columns: 4, - grid_min_columns: 2, - grid_rows: 3, - grid_min_rows: 3, + columns: 12, + rows: 3, + min_columns: 6, + min_rows: 3, }; } return { - grid_columns: 4, - grid_min_columns: 2, - grid_rows: 2, - grid_min_rows: 2, + columns: 12, + rows: 2, + min_columns: 6, + min_rows: 2, }; } diff --git a/src/panels/lovelace/components/hui-card-edit-mode.ts b/src/panels/lovelace/components/hui-card-edit-mode.ts index c599910cce71..9775e67e734a 100644 --- a/src/panels/lovelace/components/hui-card-edit-mode.ts +++ b/src/panels/lovelace/components/hui-card-edit-mode.ts @@ -233,7 +233,7 @@ export class HuiCardEditMode extends LitElement { } private _handleAction(ev) { - switch (ev.target.action) { + switch (ev.currentTarget.action) { case "edit": this._editCard(); break; diff --git a/src/translations/en.json b/src/translations/en.json index 09c4d2b1a83d..2762e217884c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2492,10 +2492,15 @@ "show_full_logs": "Show full logs", "select_number_of_lines": "Select number of lines to download", "lines": "Lines", - "download_full_log": "Download full log", + "download_logs": "Download logs", "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.", + "haos_boots_title": "Logs of HAOS startup", + "show_haos_boots": "Show HAOS startups", + "hide_haos_boots": "Hide HAOS startups", + "full_width": "Full width", + "wrap_lines": "Wrap lines", "current": "Current", "previous": "Previous", "startups_ago": "{boot} startups ago", @@ -3581,7 +3586,9 @@ "stopped_unknown_reason": "Stopped because of unknown reason {reason} at {time} (runtime: {executiontime} seconds)", "disabled": "(disabled)", "triggered_by": "{triggeredBy, select, \n alias {{alias} triggered}\n other {Triggered} \n} {triggeredPath, select, \n trigger {by the {trigger}}\n other {manually} \n} at {time}", - "path_error": "Unable to extract path {path}. Download trace and report as bug." + "path_error": "Unable to extract path {path}. Download trace and report as bug.", + "not_all_entries_are_related_automation_note": "Not all shown logbook entries might be related to this automation.", + "not_all_entries_are_related_script_note": "Not all shown logbook entries might be related to this script." } } }, @@ -7848,6 +7855,62 @@ "restore": "[%key:ui::components::data-table::settings::restore%]" } } + }, + "panel": { + "config": { + "logs": { + "caption": "[%key:ui::panel::config::logs::caption%]", + "description": "[%key:ui::panel::config::logs::description%]", + "details": "[%key:ui::panel::config::logs::details%]", + "search": "[%key:ui::panel::config::logs::search%]", + "failed_get_logs": "[%key:ui::panel::config::logs::failed_get_logs%]", + "no_issues_search": "[%key:ui::panel::config::logs::no_issues_search%]", + "load_logs": "[%key:ui::panel::config::logs::load_logs%]", + "nr_of_lines": "[%key:ui::panel::config::logs::nr_of_lines%]", + "loading_log": "[%key:ui::panel::config::logs::loading_log%]", + "no_errors": "[%key:ui::panel::config::logs::no_errors%]", + "no_issues": "[%key:ui::panel::config::logs::no_issues%]", + "clear": "[%key:ui::panel::config::logs::clear%]", + "refresh": "[%key:ui::panel::config::logs::refresh%]", + "copy": "[%key:ui::panel::config::logs::copy%]", + "log_provider": "[%key:ui::panel::config::logs::log_provider%]", + "multiple_messages": "[%key:ui::panel::config::logs::multiple_messages%]", + "level": { + "critical": "[%key:ui::panel::config::logs::level::critical%]", + "error": "[%key:ui::panel::config::logs::level::error%]", + "warning": "[%key:ui::panel::config::logs::level::warning%]", + "info": "[%key:ui::panel::config::logs::level::info%]", + "debug": "[%key:ui::panel::config::logs::level::debug%]" + }, + "custom_integration": "[%key:ui::panel::config::logs::custom_integration%]", + "error_from_custom_integration": "[%key:ui::panel::config::logs::error_from_custom_integration%]", + "show_full_logs": "[%key:ui::panel::config::logs::show_full_logs%]", + "select_number_of_lines": "[%key:ui::panel::config::logs::select_number_of_lines%]", + "lines": "[%key:ui::panel::config::logs::lines%]", + "download_logs": "[%key:ui::panel::config::logs::download_logs%]", + "scroll_down_button": "[%key:ui::panel::config::logs::scroll_down_button%]", + "provider_not_found": "[%key:ui::panel::config::logs::provider_not_found%]", + "provider_not_available": "[%key:ui::panel::config::logs::provider_not_available%]", + "haos_boots_title": "[%key:ui::panel::config::logs::haos_boots_title%]", + "show_haos_boots": "[%key:ui::panel::config::logs::show_haos_boots%]", + "hide_haos_boots": "[%key:ui::panel::config::logs::hide_haos_boots%]", + "full_width": "[%key:ui::panel::config::logs::full_width%]", + "wrap_lines": "[%key:ui::panel::config::logs::wrap_lines%]", + "current": "[%key:ui::panel::config::logs::current%]", + "previous": "[%key:ui::panel::config::logs::previous%]", + "startups_ago": "[%key:ui::panel::config::logs::startups_ago%]", + "detail": { + "logger": "[%key:ui::panel::config::logs::detail::logger%]", + "source": "[%key:ui::panel::config::logs::detail::source%]", + "integration": "[%key:ui::panel::config::integrations::integration%]", + "documentation": "[%key:ui::panel::config::logs::detail::documentation%]", + "issues": "[%key:ui::panel::config::logs::detail::issues%]", + "first_occurred": "[%key:ui::panel::config::logs::detail::first_occurred%]", + "occurrences": "[%key:ui::panel::config::logs::detail::occurrences%]", + "last_logged": "[%key:ui::panel::config::logs::detail::last_logged%]" + } + } + } } } } diff --git a/src/util/file_download.ts b/src/util/file_download.ts index f163b146a55c..dc9ba6b54ad7 100644 --- a/src/util/file_download.ts +++ b/src/util/file_download.ts @@ -1,3 +1,6 @@ +import type { HomeAssistant } from "../types"; +import { isIosApp } from "./is_ios"; + export const fileDownload = (href: string, filename = ""): void => { const a = document.createElement("a"); a.target = "_blank"; @@ -8,3 +11,6 @@ export const fileDownload = (href: string, filename = ""): void => { a.dispatchEvent(new MouseEvent("click")); document.body.removeChild(a); }; + +export const downloadFileSupported = (hass: HomeAssistant): boolean => + !isIosApp(hass) || !!hass.auth.external?.config.downloadFileSupported; diff --git a/src/util/is_ios.ts b/src/util/is_ios.ts new file mode 100644 index 000000000000..b7f72ed2f562 --- /dev/null +++ b/src/util/is_ios.ts @@ -0,0 +1,5 @@ +import type { HomeAssistant } from "../types"; +import { isSafari } from "./is_safari"; + +export const isIosApp = (hass: HomeAssistant): boolean => + isSafari && !!hass.auth.external;