From 6bd7788815a47b812ed20f139eaa403354f6d006 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 5 Nov 2024 18:58:19 +0100 Subject: [PATCH 01/12] Use grid options instead of layout options for all cards. (#22676) * Use grid options for all cards * Improve min columns for some cards * Fix button and area card --- .../energy/hui-energy-date-selection-card.ts | 10 +++---- src/panels/lovelace/cards/hui-area-card.ts | 9 +++--- src/panels/lovelace/cards/hui-button-card.ts | 17 ++++++----- src/panels/lovelace/cards/hui-entity-card.ts | 12 ++++---- src/panels/lovelace/cards/hui-heading-card.ts | 9 +++--- .../lovelace/cards/hui-humidifier-card.ts | 24 ++++++++-------- src/panels/lovelace/cards/hui-iframe-card.ts | 11 ++++---- src/panels/lovelace/cards/hui-map-card.ts | 18 ++++++------ src/panels/lovelace/cards/hui-sensor-card.ts | 12 ++++---- .../lovelace/cards/hui-statistic-card.ts | 12 ++++---- .../lovelace/cards/hui-thermostat-card.ts | 24 ++++++++-------- src/panels/lovelace/cards/hui-tile-card.ts | 24 ++++++++-------- .../cards/hui-weather-forecast-card.ts | 28 +++++++++---------- 13 files changed, 108 insertions(+), 102 deletions(-) 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 8db4e6b44a..5e021d53da 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 1e2b335ade..e8c0eb8cea 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 2bed7c6038..2fbb0adb2f 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 b2b8495255..6e5b30e2b4 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 6d57a1ae06..307fc6bfdb 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 c342934afc..15bee2a43d 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 aa078608ce..72188af3ba 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 9931303f93..f123422572 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 0bc454e3c5..db964f9e0d 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 e0ee0c079e..7fafcf8a7d 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 f96264e75e..f33d1dbdfe 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 820926d4bb..457363681f 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 5fc2ec0504..ca8fc571af 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, }; } From df3e4576db3aeb191077044cea9c82ea4ed5d9ab Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 6 Nov 2024 00:55:17 -0800 Subject: [PATCH 02/12] More flexible translation keys for logbook binary sensors (#22257) --- src/data/logbook.ts | 114 ++++++--------------------------------- src/translations/en.json | 62 ++++++++++++++++++++- 2 files changed, 77 insertions(+), 99 deletions(-) diff --git a/src/data/logbook.ts b/src/data/logbook.ts index 712680ae90..1762ffdd04 100644 --- a/src/data/logbook.ts +++ b/src/data/logbook.ts @@ -218,114 +218,32 @@ export const localizeStateMessage = ( const isOff = state === BINARY_STATE_OFF; const device_class = stateObj.attributes.device_class; - switch (device_class) { - case "battery": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_low`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_normal`); - } - break; - - case "connectivity": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_connected`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_disconnected`); - } - break; - - case "door": - case "garage_door": - case "opening": - case "window": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_opened`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_closed`); - } - break; - - case "lock": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_unlocked`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_locked`); - } - break; - - case "plug": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_plugged_in`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_unplugged`); - } - break; - - case "presence": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_at_home`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_away`); - } - break; - - case "safety": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_unsafe`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.was_safe`); - } - break; - - case "cold": - case "gas": - case "heat": - case "moisture": - case "motion": - case "occupancy": - case "power": - case "problem": - case "smoke": - case "sound": - case "vibration": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.detected_device_class`, { + if (device_class && (isOn || isOff)) { + return ( + localize( + `${LOGBOOK_LOCALIZE_PATH}.${isOn ? "detected_device_classes" : "cleared_device_classes"}.${device_class}`, + { device_class: autoCaseNoun( localize( `component.binary_sensor.entity_component.${device_class}.name` - ), + ) || device_class, hass.language ), - }); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.cleared_device_class`, { + } + ) || + // If there's no key for a specific device class, fallback to generic string + localize( + `${LOGBOOK_LOCALIZE_PATH}.${isOn ? "detected_device_class" : "cleared_device_class"}`, + { device_class: autoCaseNoun( localize( `component.binary_sensor.entity_component.${device_class}.name` - ), + ) || device_class, hass.language ), - }); - } - break; - - case "tamper": - if (isOn) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.detected_tampering`); - } - if (isOff) { - return localize(`${LOGBOOK_LOCALIZE_PATH}.cleared_tampering`); - } - break; + } + ) + ); } break; diff --git a/src/translations/en.json b/src/translations/en.json index 09c4d2b1a8..381a70779a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -486,7 +486,67 @@ "cleared_tampering": "cleared tampering", "detected_event": "{event_type} event detected", "detected_event_no_type": "detected an event", - "detected_unknown_event": "detected an unknown event" + "detected_unknown_event": "detected an unknown event", + "detected_device_classes": { + "battery": "[%key:ui::components::logbook::messages::was_low%]", + "battery_charging": "started charging", + "carbon_monoxide": "[%key:ui::components::logbook::messages::detected_device_class%]", + "cold": "[%key:ui::components::logbook::messages::detected_device_class%]", + "connectivity": "[%key:ui::components::logbook::messages::was_connected%]", + "door": "[%key:ui::components::logbook::messages::was_opened%]", + "garage_door": "[%key:ui::components::logbook::messages::was_opened%]", + "gas": "[%key:ui::components::logbook::messages::detected_device_class%]", + "heat": "[%key:ui::components::logbook::messages::detected_device_class%]", + "light": "[%key:ui::components::logbook::messages::detected_device_class%]", + "lock": "[%key:ui::components::logbook::messages::was_unlocked%]", + "moisture": "[%key:ui::components::logbook::messages::detected_device_class%]", + "motion": "[%key:ui::components::logbook::messages::detected_device_class%]", + "moving": "[%key:ui::components::logbook::messages::detected_device_class%]", + "occupancy": "[%key:ui::components::logbook::messages::detected_device_class%]", + "opening": "[%key:ui::components::logbook::messages::was_opened%]", + "plug": "[%key:ui::components::logbook::messages::was_plugged_in%]", + "power": "[%key:ui::components::logbook::messages::detected_device_class%]", + "presence": "[%key:ui::components::logbook::messages::was_at_home%]", + "problem": "[%key:ui::components::logbook::messages::detected_device_class%]", + "running": "[%key:ui::components::logbook::messages::detected_device_class%]", + "safety": "[%key:ui::components::logbook::messages::was_unsafe%]", + "smoke": "[%key:ui::components::logbook::messages::detected_device_class%]", + "sound": "[%key:ui::components::logbook::messages::detected_device_class%]", + "tamper": "[%key:ui::components::logbook::messages::detected_tampering%]", + "update": "[%key:ui::components::logbook::messages::detected_device_class%]", + "vibration": "[%key:ui::components::logbook::messages::detected_device_class%]", + "window": "[%key:ui::components::logbook::messages::was_opened%]" + }, + "cleared_device_classes": { + "battery": "[%key:ui::components::logbook::messages::was_normal%]", + "battery_charging": "stopped charging", + "carbon_monoxide": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "cold": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "connectivity": "[%key:ui::components::logbook::messages::was_disconnected%]", + "door": "[%key:ui::components::logbook::messages::was_closed%]", + "garage_door": "[%key:ui::components::logbook::messages::was_closed%]", + "gas": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "heat": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "light": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "lock": "[%key:ui::components::logbook::messages::was_locked%]", + "moisture": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "motion": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "moving": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "occupancy": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "opening": "[%key:ui::components::logbook::messages::was_closed%]", + "plug": "[%key:ui::components::logbook::messages::was_unplugged%]", + "power": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "presence": "[%key:ui::components::logbook::messages::was_away%]", + "problem": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "running": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "safety": "[%key:ui::components::logbook::messages::was_safe%]", + "smoke": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "sound": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "tamper": "[%key:ui::components::logbook::messages::cleared_tampering%]", + "update": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "vibration": "[%key:ui::components::logbook::messages::cleared_device_class%]", + "window": "[%key:ui::components::logbook::messages::was_closed%]" + } } }, "entity": { From 0488d199ac2d43bf58686c949cc24b1d750c64cc Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:03:41 +0100 Subject: [PATCH 03/12] Localize automation logbook text (#22685) * localize automation text * localize domain * fixup * split up * fixup * prettier --- src/components/trace/ha-trace-logbook.ts | 5 ++++- src/components/trace/ha-trace-path-details.ts | 5 ++++- src/components/trace/ha-trace-timeline.ts | 5 ++++- src/components/trace/hat-logbook-note.ts | 18 +++++++++++++----- src/translations/en.json | 4 +++- 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/components/trace/ha-trace-logbook.ts b/src/components/trace/ha-trace-logbook.ts index b0007d2847..08158cb2bc 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 2a38ca6504..410f05bd7b 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 7c62b3fbb0..07e7fc3600 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 1d790a21af..bda976fe28 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/translations/en.json b/src/translations/en.json index 381a70779a..9ea5723459 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3641,7 +3641,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." } } }, From c3b7ce8dc466a129455ab69c499dc0b2d574d6db Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 6 Nov 2024 10:30:19 +0100 Subject: [PATCH 04/12] Revert "More flexible translation keys for logbook binary sensors" (#22687) Revert "More flexible translation keys for logbook binary sensors (#22257)" This reverts commit df3e4576db3aeb191077044cea9c82ea4ed5d9ab. --- src/data/logbook.ts | 114 +++++++++++++++++++++++++++++++++------ src/translations/en.json | 62 +-------------------- 2 files changed, 99 insertions(+), 77 deletions(-) diff --git a/src/data/logbook.ts b/src/data/logbook.ts index 1762ffdd04..712680ae90 100644 --- a/src/data/logbook.ts +++ b/src/data/logbook.ts @@ -218,32 +218,114 @@ export const localizeStateMessage = ( const isOff = state === BINARY_STATE_OFF; const device_class = stateObj.attributes.device_class; - if (device_class && (isOn || isOff)) { - return ( - localize( - `${LOGBOOK_LOCALIZE_PATH}.${isOn ? "detected_device_classes" : "cleared_device_classes"}.${device_class}`, - { + switch (device_class) { + case "battery": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_low`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_normal`); + } + break; + + case "connectivity": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_connected`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_disconnected`); + } + break; + + case "door": + case "garage_door": + case "opening": + case "window": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_opened`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_closed`); + } + break; + + case "lock": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_unlocked`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_locked`); + } + break; + + case "plug": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_plugged_in`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_unplugged`); + } + break; + + case "presence": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_at_home`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_away`); + } + break; + + case "safety": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_unsafe`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.was_safe`); + } + break; + + case "cold": + case "gas": + case "heat": + case "moisture": + case "motion": + case "occupancy": + case "power": + case "problem": + case "smoke": + case "sound": + case "vibration": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.detected_device_class`, { device_class: autoCaseNoun( localize( `component.binary_sensor.entity_component.${device_class}.name` - ) || device_class, + ), hass.language ), - } - ) || - // If there's no key for a specific device class, fallback to generic string - localize( - `${LOGBOOK_LOCALIZE_PATH}.${isOn ? "detected_device_class" : "cleared_device_class"}`, - { + }); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.cleared_device_class`, { device_class: autoCaseNoun( localize( `component.binary_sensor.entity_component.${device_class}.name` - ) || device_class, + ), hass.language ), - } - ) - ); + }); + } + break; + + case "tamper": + if (isOn) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.detected_tampering`); + } + if (isOff) { + return localize(`${LOGBOOK_LOCALIZE_PATH}.cleared_tampering`); + } + break; } break; diff --git a/src/translations/en.json b/src/translations/en.json index 9ea5723459..0c3cd4698d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -486,67 +486,7 @@ "cleared_tampering": "cleared tampering", "detected_event": "{event_type} event detected", "detected_event_no_type": "detected an event", - "detected_unknown_event": "detected an unknown event", - "detected_device_classes": { - "battery": "[%key:ui::components::logbook::messages::was_low%]", - "battery_charging": "started charging", - "carbon_monoxide": "[%key:ui::components::logbook::messages::detected_device_class%]", - "cold": "[%key:ui::components::logbook::messages::detected_device_class%]", - "connectivity": "[%key:ui::components::logbook::messages::was_connected%]", - "door": "[%key:ui::components::logbook::messages::was_opened%]", - "garage_door": "[%key:ui::components::logbook::messages::was_opened%]", - "gas": "[%key:ui::components::logbook::messages::detected_device_class%]", - "heat": "[%key:ui::components::logbook::messages::detected_device_class%]", - "light": "[%key:ui::components::logbook::messages::detected_device_class%]", - "lock": "[%key:ui::components::logbook::messages::was_unlocked%]", - "moisture": "[%key:ui::components::logbook::messages::detected_device_class%]", - "motion": "[%key:ui::components::logbook::messages::detected_device_class%]", - "moving": "[%key:ui::components::logbook::messages::detected_device_class%]", - "occupancy": "[%key:ui::components::logbook::messages::detected_device_class%]", - "opening": "[%key:ui::components::logbook::messages::was_opened%]", - "plug": "[%key:ui::components::logbook::messages::was_plugged_in%]", - "power": "[%key:ui::components::logbook::messages::detected_device_class%]", - "presence": "[%key:ui::components::logbook::messages::was_at_home%]", - "problem": "[%key:ui::components::logbook::messages::detected_device_class%]", - "running": "[%key:ui::components::logbook::messages::detected_device_class%]", - "safety": "[%key:ui::components::logbook::messages::was_unsafe%]", - "smoke": "[%key:ui::components::logbook::messages::detected_device_class%]", - "sound": "[%key:ui::components::logbook::messages::detected_device_class%]", - "tamper": "[%key:ui::components::logbook::messages::detected_tampering%]", - "update": "[%key:ui::components::logbook::messages::detected_device_class%]", - "vibration": "[%key:ui::components::logbook::messages::detected_device_class%]", - "window": "[%key:ui::components::logbook::messages::was_opened%]" - }, - "cleared_device_classes": { - "battery": "[%key:ui::components::logbook::messages::was_normal%]", - "battery_charging": "stopped charging", - "carbon_monoxide": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "cold": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "connectivity": "[%key:ui::components::logbook::messages::was_disconnected%]", - "door": "[%key:ui::components::logbook::messages::was_closed%]", - "garage_door": "[%key:ui::components::logbook::messages::was_closed%]", - "gas": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "heat": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "light": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "lock": "[%key:ui::components::logbook::messages::was_locked%]", - "moisture": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "motion": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "moving": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "occupancy": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "opening": "[%key:ui::components::logbook::messages::was_closed%]", - "plug": "[%key:ui::components::logbook::messages::was_unplugged%]", - "power": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "presence": "[%key:ui::components::logbook::messages::was_away%]", - "problem": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "running": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "safety": "[%key:ui::components::logbook::messages::was_safe%]", - "smoke": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "sound": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "tamper": "[%key:ui::components::logbook::messages::cleared_tampering%]", - "update": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "vibration": "[%key:ui::components::logbook::messages::cleared_device_class%]", - "window": "[%key:ui::components::logbook::messages::was_closed%]" - } + "detected_unknown_event": "detected an unknown event" } }, "entity": { From 38da01abfac766af72dac82d0263918380b4bff9 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 6 Nov 2024 10:42:38 +0100 Subject: [PATCH 05/12] Fix icon click in edit card overflow (#22686) --- src/panels/lovelace/components/hui-card-edit-mode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/components/hui-card-edit-mode.ts b/src/panels/lovelace/components/hui-card-edit-mode.ts index c599910cce..9775e67e73 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; From e908fbb48e262cb1068d1e3f90fc3739ac825c4f Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:42:46 +0100 Subject: [PATCH 06/12] Check for empty logs (#22675) * Fix download logs default lines + translations + iOS, add live logs indicator * Fix rtl in error-log-card * Fix downloadFileSupported --- src/external_app/external_messaging.ts | 1 + .../config/logs/dialog-download-logs.ts | 16 ++- src/panels/config/logs/error-log-card.ts | 129 ++++++++++++++++-- src/translations/en.json | 2 +- src/util/file_download.ts | 6 + src/util/is_ios.ts | 5 + 6 files changed, 137 insertions(+), 22 deletions(-) create mode 100644 src/util/is_ios.ts diff --git a/src/external_app/external_messaging.ts b/src/external_app/external_messaging.ts index a870b72be2..292746bdee 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/logs/dialog-download-logs.ts b/src/panels/config/logs/dialog-download-logs.ts index 0a7e9d965f..801ef1db22 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 259a87ca7d..d7d2bc430e 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -1,6 +1,7 @@ import "@material/mwc-list/mwc-list-item"; import { mdiArrowCollapseDown, + mdiCircle, mdiDownload, mdiMenuDown, mdiRefresh, @@ -40,10 +41,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"; @@ -109,6 +114,10 @@ class ErrorLogCard extends LitElement { @state() private _boots?: number[]; + @state() private _downloadSupported; + + @state() private _logsFileLink; + protected render(): TemplateResult { return html`
@@ -176,13 +185,32 @@ class ErrorLogCard extends LitElement { ` : nothing} - + ${this._downloadSupported + ? html` + + ` + : this._logsFileLink + ? html` + + + + ` + : nothing} ${!this._streamSupported || this._error ? html` + ${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` + + + ${this.hass.localize( + "ui.panel.config.logs.download_logs" + )} + + ` + : nothing} ${this.hass.localize("ui.panel.config.logs.load_logs")} @@ -268,6 +310,9 @@ class ErrorLogCard extends LitElement { 11 ); } + if (this._downloadSupported === undefined && this.hass) { + this._downloadSupported = downloadFileSupported(this.hass); + } } protected firstUpdated(changedProps: PropertyValues) { @@ -331,7 +376,7 @@ class ErrorLogCard extends LitElement { ); } - private async _downloadFullLog(): Promise { + private async _downloadLogs(): Promise { if (this._streamSupported) { showDownloadLogsDialog(this, { header: this.header, @@ -378,6 +423,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 +495,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 { @@ -597,6 +665,9 @@ class ErrorLogCard extends LitElement { } static styles: CSSResultGroup = css` + :host { + direction: var(--direction); + } .error-log-intro { text-align: center; margin: 16px; @@ -646,7 +717,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 +784,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/translations/en.json b/src/translations/en.json index 0c3cd4698d..248a77bebc 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2492,7 +2492,7 @@ "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.", diff --git a/src/util/file_download.ts b/src/util/file_download.ts index f163b146a5..dc9ba6b54a 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 0000000000..b7f72ed2f5 --- /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; From 4bd70167add8b4bf7911b2fb7de1e9e3db709766 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:59:01 +0100 Subject: [PATCH 07/12] Add overflow menu to error-log-card (#22684) * Add overflow menu to error-log-card * Add toggle line wrap icon-button in error-log-card --- src/components/ha-ansi-to-html.ts | 10 +++- src/panels/config/logs/error-log-card.ts | 59 ++++++++++++++++++++++-- src/translations/en.json | 5 ++ 3 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/components/ha-ansi-to-html.ts b/src/components/ha-ansi-to-html.ts index ed0b3773e3..5956303ab4 100644 --- a/src/components/ha-ansi-to-html.ts +++ b/src/components/ha-ansi-to-html.ts @@ -12,6 +12,7 @@ import { query, state as litState, } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; interface State { bold: boolean; @@ -26,12 +27,15 @@ interface State { export class HaAnsiToHtml extends LitElement { @property() public content!: string; + @property({ type: Boolean, attribute: "wrap-disabled" }) public wrapDisabled = + false; + @query("pre") private _pre?: HTMLPreElement; @litState() private _filter = ""; protected render(): TemplateResult | void { - return html`
`;
+    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/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts
index d7d2bc430e..b89288d720 100644
--- a/src/panels/config/logs/error-log-card.ts
+++ b/src/panels/config/logs/error-log-card.ts
@@ -1,10 +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,
@@ -32,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";
 
@@ -114,6 +122,10 @@ class ErrorLogCard extends LitElement {
 
   @state() private _boots?: number[];
 
+  @state() private _showBootsSelect = false;
+
+  @state() private _wrapLines = true;
+
   @state() private _downloadSupported;
 
   @state() private _logsFileLink;
@@ -123,7 +135,7 @@ class ErrorLogCard extends LitElement {
       
${this._error ? html`${this._error}` - : ""} + : nothing}

@@ -131,9 +143,14 @@ class ErrorLogCard extends LitElement { this.hass.localize("ui.panel.config.logs.show_full_logs")}

- ${this._streamSupported && Array.isArray(this._boots) + ${this._streamSupported && + Array.isArray(this._boots) && + this._showBootsSelect ? html` ` : nothing} + ${!this._streamSupported || this._error ? html`` : nothing} + + + + + + ${this.hass.localize( + `ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots` + )} + +
@@ -248,7 +285,9 @@ class ErrorLogCard extends LitElement { )}
` : nothing} - +
` - : ""} + : nothing}
`; } @@ -653,6 +692,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; diff --git a/src/translations/en.json b/src/translations/en.json index 248a77bebc..5165f03d28 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2496,6 +2496,11 @@ "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", From 5430040b96601a8721f18fb78ad42cabed3d8472 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 6 Nov 2024 11:17:14 +0100 Subject: [PATCH 08/12] Fix update more info margin (#22691) --- .../more-info/controls/more-info-update.ts | 111 ++++++++++-------- 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/src/dialogs/more-info/controls/more-info-update.ts b/src/dialogs/more-info/controls/more-info-update.ts index 7014a505e9..5fe6d6d792 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.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.installed_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.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; From 04a45a4361cb0d3f078b22c1ad243ab97863097d Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:17:36 +0100 Subject: [PATCH 09/12] Fix action descriptions switch to English when using search (#22689) * var mistake * Update src/panels/config/automation/add-automation-element-dialog.ts * prettier --- src/panels/config/automation/add-automation-element-dialog.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/automation/add-automation-element-dialog.ts b/src/panels/config/automation/add-automation-element-dialog.ts index 99e497a70b..c5d298e44d 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, }); } From ed3096157cdd44e128bb85c4d726602042ecb58b Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:19:17 +0100 Subject: [PATCH 10/12] Fix logs overflow when there are 0 boots (#22694) --- src/panels/config/logs/error-log-card.ts | 30 ++++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/panels/config/logs/error-log-card.ts b/src/panels/config/logs/error-log-card.ts index b89288d720..94039c102e 100644 --- a/src/panels/config/logs/error-log-card.ts +++ b/src/panels/config/logs/error-log-card.ts @@ -242,19 +242,23 @@ class ErrorLogCard extends LitElement { .label=${this.hass.localize("ui.common.refresh")} >` : nothing} - - - - - - ${this.hass.localize( - `ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots` - )} - - + ${this._streamSupported && Array.isArray(this._boots) + ? html` + + + + + + ${this.hass.localize( + `ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots` + )} + + + ` + : nothing}
From 26e914290dccc014594136964411c528cd3a8953 Mon Sep 17 00:00:00 2001 From: Wendelin <12148533+wendevlin@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:33:45 +0100 Subject: [PATCH 11/12] Fix hassio logs translations (#22693) Co-authored-by: Bram Kragten --- .../addon-view/log/hassio-addon-log-tab.ts | 3 +- src/panels/config/logs/error-log-card.ts | 76 +++++++++---------- src/translations/en.json | 56 ++++++++++++++ 3 files changed, 92 insertions(+), 43 deletions(-) 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 9f52d3e832..a40454fe83 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")} >
; + @property() public filter = ""; @property() public header?: string; @@ -131,6 +134,7 @@ class ErrorLogCard extends LitElement { @state() private _logsFileLink; protected render(): TemplateResult { + const localize = this.localizeFunc || this.hass.localize; return html`
${this._error @@ -139,8 +143,7 @@ class ErrorLogCard extends LitElement {

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

${this._streamSupported && @@ -148,17 +151,16 @@ class ErrorLogCard extends LitElement { this._showBootsSelect ? html` @@ -180,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 } )} @@ -207,9 +205,7 @@ class ErrorLogCard extends LitElement { ` : this._logsFileLink @@ -221,7 +217,7 @@ class ErrorLogCard extends LitElement { > @@ -231,7 +227,7 @@ class ErrorLogCard extends LitElement { @@ -239,7 +235,7 @@ class ErrorLogCard extends LitElement { ? html`` : nothing} ${this._streamSupported && Array.isArray(this._boots) @@ -252,7 +248,7 @@ class ErrorLogCard extends LitElement { slot="graphic" .path=${mdiFormatListNumbered} > - ${this.hass.localize( + ${localize( `ui.panel.config.logs.${this._showBootsSelect ? "hide" : "show"}_haos_boots` )} @@ -271,22 +267,17 @@ 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.hass.localize( - "ui.panel.config.logs.download_logs" - )} + ${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} @@ -572,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), + } + ); } } diff --git a/src/translations/en.json b/src/translations/en.json index 5165f03d28..2762e21788 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -7855,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%]" + } + } + } } } } From 5247b74fd40ab4d080a31eaadbacede0fabf4f7a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 6 Nov 2024 13:43:57 +0100 Subject: [PATCH 12/12] Bumped version to 20241106.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ad1d211e00..eeaa6db063 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20241105.0" +version = "20241106.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md"