From 6d1ea414492a3a9530ddcb1ba805919816e67f29 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 28 Jun 2023 08:50:25 -0700 Subject: [PATCH 01/18] Fab spacer for zha table (#17082) --- .../integrations/integration-panels/zha/zha-groups-dashboard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/config/integrations/integration-panels/zha/zha-groups-dashboard.ts b/src/panels/config/integrations/integration-panels/zha/zha-groups-dashboard.ts index 39f77d934b..0b756fe41d 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-groups-dashboard.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-groups-dashboard.ts @@ -119,6 +119,7 @@ export class ZHAGroupsDashboard extends LitElement { .data=${this._formattedGroups(this._groups)} @row-click=${this._handleRowClicked} clickable + hasFab > Date: Thu, 29 Jun 2023 09:40:09 +0200 Subject: [PATCH 02/18] Remove weird colon from integration setup error text (#17086) --- src/panels/config/integrations/ha-config-integration-page.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index cc4819c05f..45292826bc 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -543,8 +543,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ]; if (item.reason) { this.hass.loadBackendTranslation("config", item.domain); - stateTextExtra = html`: - ${this.hass.localize( + stateTextExtra = html`${this.hass.localize( `component.${item.domain}.config.error.${item.reason}` ) || item.reason}`; } else { From 82cc66701234863f7cdf78daf9d938c22f6a6d61 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Thu, 29 Jun 2023 09:40:55 +0200 Subject: [PATCH 03/18] Add min/max support for prompt dialog (e.g. used for card moving) (#17085) --- src/dialogs/generic/dialog-box.ts | 2 ++ src/dialogs/generic/show-dialog-box.ts | 2 ++ src/panels/lovelace/components/hui-card-options.ts | 1 + 3 files changed, 5 insertions(+) diff --git a/src/dialogs/generic/dialog-box.ts b/src/dialogs/generic/dialog-box.ts index 5036664946..3b2ded7aad 100644 --- a/src/dialogs/generic/dialog-box.ts +++ b/src/dialogs/generic/dialog-box.ts @@ -81,6 +81,8 @@ class DialogBox extends LitElement { .type=${this._params.inputType ? this._params.inputType : "text"} + .min=${this._params.inputMin} + .max=${this._params.inputMax} > ` : ""} diff --git a/src/dialogs/generic/show-dialog-box.ts b/src/dialogs/generic/show-dialog-box.ts index 0b5de18606..7c95283aae 100644 --- a/src/dialogs/generic/show-dialog-box.ts +++ b/src/dialogs/generic/show-dialog-box.ts @@ -26,6 +26,8 @@ export interface PromptDialogParams extends BaseDialogBoxParams { placeholder?: string; confirm?: (out?: string) => void; cancel?: () => void; + inputMin?: number | string; + inputMax?: number | string; } export interface DialogBoxParams diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index 37d5a4d27e..f91d5218f4 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -288,6 +288,7 @@ export class HuiCardOptions extends LitElement { "ui.panel.lovelace.editor.change_position.text" ), inputType: "number", + inputMin: "1", placeholder: String(path[1] + 1), }); From b539a939b4b22d31f7255ea55f99f79c5beef0c0 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Thu, 29 Jun 2023 09:48:53 +0200 Subject: [PATCH 04/18] Pass missing time zone info to dev tool state (#17087) --- src/panels/developer-tools/state/developer-tools-state.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/panels/developer-tools/state/developer-tools-state.js b/src/panels/developer-tools/state/developer-tools-state.js index 7b8fb28753..1fe00ee0fd 100644 --- a/src/panels/developer-tools/state/developer-tools-state.js +++ b/src/panels/developer-tools/state/developer-tools-state.js @@ -628,14 +628,16 @@ class HaPanelDevState extends EventsMixin(LocalizeMixin(PolymerElement)) { lastChangedString(entity) { return formatDateTimeWithSeconds( new Date(entity.last_changed), - this.hass.locale + this.hass.locale, + this.hass.config ); } lastUpdatedString(entity) { return formatDateTimeWithSeconds( new Date(entity.last_updated), - this.hass.locale + this.hass.locale, + this.hass.config ); } From ab1759f11d6c689e849c6f7df92995b0691f2e74 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:54:18 -0700 Subject: [PATCH 05/18] Clear template-not-supported error when switching services in devtools (#17090) --- src/panels/developer-tools/service/developer-tools-service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/panels/developer-tools/service/developer-tools-service.ts b/src/panels/developer-tools/service/developer-tools-service.ts index 996f377f5f..91c10f6972 100644 --- a/src/panels/developer-tools/service/developer-tools-service.ts +++ b/src/panels/developer-tools/service/developer-tools-service.ts @@ -420,6 +420,7 @@ class HaPanelDevService extends LitElement { ev.stopPropagation(); this._serviceData = { service: ev.detail.value || "", data: {} }; this._yamlEditor?.setValue(this._serviceData); + this._checkUiSupported(); } private _fillExampleData() { @@ -441,6 +442,7 @@ class HaPanelDevService extends LitElement { }); this._serviceData = { ...this._serviceData!, data: example }; this._yamlEditor?.setValue(this._serviceData); + this._checkUiSupported(); } static get styles(): CSSResultGroup { From b96ad65f4891469ffc48ba84faaca4b936fb34b3 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 29 Jun 2023 00:57:37 -0700 Subject: [PATCH 06/18] Add summations to gas/solar/water energy graph tooltips (#17084) --- .../cards/energy/hui-energy-gas-graph-card.ts | 18 ++++++++++++++++ .../energy/hui-energy-solar-graph-card.ts | 21 +++++++++++++++++++ .../energy/hui-energy-water-graph-card.ts | 18 ++++++++++++++++ src/translations/en.json | 9 +++++++- 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts index 68ac83ab67..3a67549e4f 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-gas-graph-card.ts @@ -240,6 +240,24 @@ export class HuiEnergyGasGraphCard context.parsed.y, locale )} ${unit}`, + footer: (contexts) => { + if (contexts.length < 2) { + return []; + } + let total = 0; + for (const context of contexts) { + total += (context.dataset.data[context.dataIndex] as any).y; + } + if (total === 0) { + return []; + } + return [ + this.hass.localize( + "ui.panel.lovelace.cards.energy.energy_gas_graph.total_consumed", + { num: formatNumber(total, locale), unit } + ), + ]; + }, }, }, filler: { diff --git a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts index 778ccb77d1..b1f8da63c0 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-solar-graph-card.ts @@ -236,6 +236,27 @@ export class HuiEnergySolarGraphCard context.parsed.y, locale )} kWh`, + footer: (contexts) => { + const production_contexts = contexts.filter( + (c) => c.dataset?.stack === "solar" + ); + if (production_contexts.length < 2) { + return []; + } + let total = 0; + for (const context of production_contexts) { + total += (context.dataset.data[context.dataIndex] as any).y; + } + if (total === 0) { + return []; + } + return [ + this.hass.localize( + "ui.panel.lovelace.cards.energy.energy_solar_graph.total_produced", + { num: formatNumber(total, locale) } + ), + ]; + }, }, }, filler: { diff --git a/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts index 41d4029c04..4fc102ebf2 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-water-graph-card.ts @@ -240,6 +240,24 @@ export class HuiEnergyWaterGraphCard context.parsed.y, locale )} ${unit}`, + footer: (contexts) => { + if (contexts.length < 2) { + return []; + } + let total = 0; + for (const context of contexts) { + total += (context.dataset.data[context.dataIndex] as any).y; + } + if (total === 0) { + return []; + } + return [ + this.hass.localize( + "ui.panel.lovelace.cards.energy.energy_water_graph.total_consumed", + { num: formatNumber(total, locale), unit } + ), + ]; + }, }, }, filler: { diff --git a/src/translations/en.json b/src/translations/en.json index 2f71088f55..2c60787dbf 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4312,7 +4312,14 @@ }, "energy_solar_graph": { "production": "Production {name}", - "forecast": "Forecast {name}" + "forecast": "Forecast {name}", + "total_produced": "Total produced {num} kWh" + }, + "energy_gas_graph": { + "total_consumed": "Total consumed {num} {unit}" + }, + "energy_water_graph": { + "total_consumed": "[%key:ui::panel::lovelace::cards::energy::energy_gas_graph::total_consumed%]" }, "solar_consumed_gauge": { "card_indicates_solar_energy_used": "This card indicates how much of the solar energy you produced was used by your home instead of being returned to the grid.", From 6cc207752fb1daf5194ff387aa05e343c796d5e0 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 10:31:16 +0200 Subject: [PATCH 07/18] Empty response when changing service (#17091) --- src/panels/developer-tools/service/developer-tools-service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/developer-tools/service/developer-tools-service.ts b/src/panels/developer-tools/service/developer-tools-service.ts index 91c10f6972..66da2d5f0e 100644 --- a/src/panels/developer-tools/service/developer-tools-service.ts +++ b/src/panels/developer-tools/service/developer-tools-service.ts @@ -419,6 +419,7 @@ class HaPanelDevService extends LitElement { private _serviceChanged(ev) { ev.stopPropagation(); this._serviceData = { service: ev.detail.value || "", data: {} }; + this._response = undefined; this._yamlEditor?.setValue(this._serviceData); this._checkUiSupported(); } From 77d24f4129bbc636a7e127299434507c2dde6cce Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 29 Jun 2023 11:27:49 +0200 Subject: [PATCH 08/18] Show position on sidebar card and hide arrow on panel card (#17092) --- .../lovelace/components/hui-card-options.ts | 62 ++++++++++--------- src/panels/lovelace/views/hui-masonry-view.ts | 1 - src/panels/lovelace/views/hui-panel-view.ts | 1 + 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index f91d5218f4..ec04f21acd 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -46,7 +46,7 @@ export class HuiCardOptions extends LitElement { @queryAssignedNodes() private _assignedNodes?: NodeListOf; - @property({ type: Boolean }) public showPosition = false; + @property({ type: Boolean }) public hidePosition = false; @storage({ key: "lovelaceClipboard", @@ -82,36 +82,38 @@ export class HuiCardOptions extends LitElement { >
- - ${this.showPosition - ? html` -
${this.path![1] + 1}
-
` + ${!this.hidePosition + ? html` + + +
${this.path![1] + 1}
+
+ + ` : nothing} - Date: Thu, 29 Jun 2023 12:51:00 +0200 Subject: [PATCH 09/18] Allow to search statistic by statistics id and name (#17098) --- src/components/entity/ha-statistic-picker.ts | 59 ++++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/src/components/entity/ha-statistic-picker.ts b/src/components/entity/ha-statistic-picker.ts index cfdb725c39..fc40f169ea 100644 --- a/src/components/entity/ha-statistic-picker.ts +++ b/src/components/entity/ha-statistic-picker.ts @@ -17,6 +17,16 @@ import "../ha-combo-box"; import type { HaComboBox } from "../ha-combo-box"; import "../ha-svg-icon"; import "./state-badge"; +import { + fuzzyFilterSort, + ScorableTextItem, +} from "../../common/string/filter/sequence-matching"; + +interface StatisticItem extends ScorableTextItem { + id: string; + name: string; + state?: HassEntity; +} @customElement("ha-statistic-picker") export class HaStatisticPicker extends LitElement { @@ -75,11 +85,11 @@ export class HaStatisticPicker extends LitElement { private _init = false; - private _rowRenderer: ComboBoxLitRenderer<{ - id: string; - name: string; - state?: HassEntity; - }> = (item) => html` + private _statistics: StatisticItem[] = []; + + private _rowRenderer: ComboBoxLitRenderer = ( + item + ) => html` ${item.state ? html`` : ""} @@ -105,7 +115,7 @@ export class HaStatisticPicker extends LitElement { includeUnitClass?: string | string[], includeDeviceClass?: string | string[], entitiesOnly?: boolean - ): Array<{ id: string; name: string; state?: HassEntity }> => { + ): StatisticItem[] => { if (!statisticIds.length) { return [ { @@ -113,6 +123,7 @@ export class HaStatisticPicker extends LitElement { name: this.hass.localize( "ui.components.statistic-picker.no_statistics" ), + strings: [], }, ]; } @@ -146,26 +157,28 @@ export class HaStatisticPicker extends LitElement { }); } - const output: Array<{ - id: string; - name: string; - state?: HassEntity; - }> = []; + const output: StatisticItem[] = []; statisticIds.forEach((meta) => { const entityState = this.hass.states[meta.statistic_id]; if (!entityState) { if (!entitiesOnly) { + const id = meta.statistic_id; + const name = getStatisticLabel(this.hass, meta.statistic_id, meta); output.push({ - id: meta.statistic_id, - name: getStatisticLabel(this.hass, meta.statistic_id, meta), + id, + name, + strings: [id, name], }); } return; } + const id = meta.statistic_id; + const name = getStatisticLabel(this.hass, meta.statistic_id, meta); output.push({ - id: meta.statistic_id, - name: getStatisticLabel(this.hass, meta.statistic_id, meta), + id, + name, state: entityState, + strings: [id, name], }); }); @@ -174,6 +187,7 @@ export class HaStatisticPicker extends LitElement { { id: "", name: this.hass.localize("ui.components.statistic-picker.no_match"), + strings: [], }, ]; } @@ -189,6 +203,7 @@ export class HaStatisticPicker extends LitElement { name: this.hass.localize( "ui.components.statistic-picker.missing_entity" ), + strings: [], }); return output; @@ -216,7 +231,7 @@ export class HaStatisticPicker extends LitElement { ) { this._init = true; if (this.hasUpdated) { - (this.comboBox as any).items = this._getStatistics( + this._statistics = this._getStatistics( this.statisticIds!, this.includeStatisticsUnitOfMeasurement, this.includeUnitClass, @@ -225,7 +240,7 @@ export class HaStatisticPicker extends LitElement { ); } else { this.updateComplete.then(() => { - (this.comboBox as any).items = this._getStatistics( + this._statistics = this._getStatistics( this.statisticIds!, this.includeStatisticsUnitOfMeasurement, this.includeUnitClass, @@ -248,11 +263,13 @@ export class HaStatisticPicker extends LitElement { .renderer=${this._rowRenderer} .disabled=${this.disabled} .allowCustomValue=${this.allowCustomEntity} + .filteredItems=${this._statistics} item-value-path="id" item-id-path="id" item-label-path="name" @opened-changed=${this._openedChanged} @value-changed=${this._statisticChanged} + @filter-changed=${this._filterChanged} > `; } @@ -281,6 +298,14 @@ export class HaStatisticPicker extends LitElement { this._opened = ev.detail.value; } + private _filterChanged(ev: CustomEvent): void { + const target = ev.target as HaComboBox; + const filterString = ev.detail.value.toLowerCase(); + target.filteredItems = filterString.length + ? fuzzyFilterSort(filterString, this._statistics) + : this._statistics; + } + private _setValue(value: string) { this.value = value; setTimeout(() => { From 82ae04e070317ffb25fe8991b2d3f4bc6929b377 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 12:51:14 +0200 Subject: [PATCH 10/18] Integration dashboard: don't stretch items (#17099) --- .../integrations/ha-config-integrations-dashboard.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integrations-dashboard.ts b/src/panels/config/integrations/ha-config-integrations-dashboard.ts index 179167cd22..7f2dc56af6 100644 --- a/src/panels/config/integrations/ha-config-integrations-dashboard.ts +++ b/src/panels/config/integrations/ha-config-integrations-dashboard.ts @@ -746,16 +746,13 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { } .container { display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - grid-gap: 16px 16px; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + grid-gap: 8px 8px; padding: 8px 16px 16px; } .container:last-of-type { margin-bottom: 64px; } - .container > * { - max-width: 500px; - } .empty-message { margin: auto; text-align: center; From b5eb18e163761ec06430bbfd5c3b6a96635255de Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Thu, 29 Jun 2023 14:53:08 +0200 Subject: [PATCH 11/18] Ensure domain titles are loaded for quick reload items (#17100) --- src/dialogs/quick-bar/ha-quick-bar.ts | 11 ++++++++--- src/types.ts | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 30abaf7de3..5db6f06289 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -501,7 +501,7 @@ export class QuickBar extends LitElement { private async _generateCommandItems(): Promise { return [ - ...this._generateReloadCommands(), + ...(await this._generateReloadCommands()), ...this._generateServerControlCommands(), ...(await this._generateNavigationCommands()), ].sort((a, b) => @@ -513,17 +513,22 @@ export class QuickBar extends LitElement { ); } - private _generateReloadCommands(): CommandItem[] { + private async _generateReloadCommands(): Promise { // Get all domains that have a direct "reload" service const reloadableDomains = componentsWithService(this.hass, "reload"); + const localize = await this.hass.loadBackendTranslation( + "title", + reloadableDomains + ); + const commands = reloadableDomains.map((domain) => ({ primaryText: this.hass.localize(`ui.dialogs.quick-bar.commands.reload.${domain}`) || this.hass.localize( "ui.dialogs.quick-bar.commands.reload.reload", "domain", - domainToName(this.hass.localize, domain) + domainToName(localize, domain) ), action: () => this.hass.callService(domain, "reload"), iconPath: mdiReload, diff --git a/src/types.ts b/src/types.ts index 89a3213381..7dcfee537f 100644 --- a/src/types.ts +++ b/src/types.ts @@ -251,7 +251,7 @@ export interface HomeAssistant { callWS(msg: MessageBase): Promise; loadBackendTranslation( category: Parameters[2], - integration?: Parameters[3], + integrations?: Parameters[3], configFlow?: Parameters[4] ): Promise; loadFragmentTranslation(fragment: string): Promise; From a227d7a2cf45d8669a8f12eb3ad5376c1b6db9bc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 15:09:58 +0200 Subject: [PATCH 12/18] Fix ha-menu-button not hidden (#17102) --- src/components/ha-menu-button.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/ha-menu-button.ts b/src/components/ha-menu-button.ts index 2246dd136b..0e7fac5aa3 100644 --- a/src/components/ha-menu-button.ts +++ b/src/components/ha-menu-button.ts @@ -1,6 +1,6 @@ import { mdiMenu } from "@mdi/js"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { subscribeNotifications } from "../data/persistent_notification"; @@ -17,6 +17,8 @@ class HaMenuButton extends LitElement { @state() private _hasNotifications = false; + @state() private _show = false; + private _alwaysVisible = false; private _attachNotifOnConnect = false; @@ -40,7 +42,10 @@ class HaMenuButton extends LitElement { } } - protected render(): TemplateResult { + protected render() { + if (!this._show) { + return nothing; + } const hasNotifications = this._hasNotifications && (this.narrow || this.hass.dockedSidebar === "always_hidden"); @@ -66,8 +71,8 @@ class HaMenuButton extends LitElement { (Number((window.parent as any).frontendVersion) || 0) < 20190710; } - protected updated(changedProps) { - super.updated(changedProps); + protected willUpdate(changedProps) { + super.willUpdate(changedProps); if (!changedProps.has("narrow") && !changedProps.has("hass")) { return; @@ -85,11 +90,11 @@ class HaMenuButton extends LitElement { const showButton = this.narrow || this.hass.dockedSidebar === "always_hidden"; - if (oldShowButton === showButton) { + if (this.hasUpdated && oldShowButton === showButton) { return; } - this.style.display = showButton || this._alwaysVisible ? "initial" : "none"; + this._show = showButton || this._alwaysVisible; if (!showButton) { if (this._unsubNotifications) { From 82a8b8fd5d63b55d6f5452efe0ec7a2eb3696369 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 29 Jun 2023 16:09:27 +0200 Subject: [PATCH 13/18] Same order in device dashboard and device page for config entries (#17104) --- src/panels/config/devices/ha-config-device-page.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index b3dbecd72c..e55733aa3f 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -145,8 +145,15 @@ export class HaConfigDevicePage extends LitElement { ); private _integrations = memoizeOne( - (device: DeviceRegistryEntry, entries: ConfigEntry[]): ConfigEntry[] => - entries.filter((entry) => device.config_entries.includes(entry.entry_id)) + (device: DeviceRegistryEntry, entries: ConfigEntry[]): ConfigEntry[] => { + const entryLookup: { [entryId: string]: ConfigEntry } = {}; + for (const entry of entries) { + entryLookup[entry.entry_id] = entry; + } + return device.config_entries + .map((entry) => entryLookup[entry]) + .filter(Boolean); + } ); private _entities = memoizeOne( From f712b76ccf9ee1e51a98fee06f7537a3ee963e81 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 16:56:24 +0200 Subject: [PATCH 14/18] Update compute_state_display.ts --- src/common/entity/compute_state_display.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts index c41bde1263..998949b007 100644 --- a/src/common/entity/compute_state_display.ts +++ b/src/common/entity/compute_state_display.ts @@ -50,7 +50,7 @@ export const computeStateDisplay = ( entities: HomeAssistant["entities"], state?: string ): string => { - const entity = entities[stateObj.entity_id] as + const entity = entities?.[stateObj.entity_id] as | EntityRegistryDisplayEntry | undefined; From a5c7f261c82d3fd68f7b91d096eb1c0ba3c4d8bc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 17:14:05 +0200 Subject: [PATCH 15/18] dont split a string --- .../trigger/types/ha-automation-trigger-conversation.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts index f5cc84d862..34a03c9ace 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-conversation.ts @@ -92,7 +92,11 @@ export class HaConversationTrigger private async _updateOption(ev: Event) { const index = (ev.target as any).index; - const command = [...this.trigger.command]; + const command = [ + ...(Array.isArray(this.trigger.command) + ? this.trigger.command + : [this.trigger.command]), + ]; command.splice(index, 1, (ev.target as HaTextField).value); fireEvent(this, "value-changed", { value: { ...this.trigger, command }, From 42d6e6dc51790ae44592ccd61e8b659a36b83cda Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 17:26:51 +0200 Subject: [PATCH 16/18] Replace integration error/warning banner with subtitle (#17103) --- .../ha-config-integrations-dashboard.ts | 2 +- .../integrations/ha-integration-card.ts | 11 ++- .../integrations/ha-integration-header.ts | 67 ++++++++++++++++--- src/translations/en.json | 2 +- 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/src/panels/config/integrations/ha-config-integrations-dashboard.ts b/src/panels/config/integrations/ha-config-integrations-dashboard.ts index 7f2dc56af6..9a39a25eab 100644 --- a/src/panels/config/integrations/ha-config-integrations-dashboard.ts +++ b/src/panels/config/integrations/ha-config-integrations-dashboard.ts @@ -746,7 +746,7 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { } .container { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); grid-gap: 8px 8px; padding: 8px 16px 16px; } diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 7866cd8343..acccb400a2 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -97,7 +97,13 @@ export class HaIntegrationCard extends LitElement { .hass=${this.hass} .domain=${this.domain} .localizedDomainName=${this.items[0].localized_domain_name} - .banner=${entryState !== "loaded" + .error=${ERROR_STATES.includes(entryState) + ? this.hass.localize( + `ui.panel.config.integrations.config_entry.state.${entryState}` + ) + : undefined} + .warning=${entryState !== "loaded" && + !ERROR_STATES.includes(entryState) ? this.hass.localize( `ui.panel.config.integrations.config_entry.state.${entryState}` ) @@ -317,9 +323,12 @@ export class HaIntegrationCard extends LitElement { --text-on-state-color: var(--primary-text-color); } .state-not-loaded { + opacity: 0.8; + --state-color: var(--warning-color); --state-message-color: var(--primary-text-color); } .state-setup { + opacity: 0.8; --state-message-color: var(--secondary-text-color); } :host(.highlight) ha-card { diff --git a/src/panels/config/integrations/ha-integration-header.ts b/src/panels/config/integrations/ha-integration-header.ts index 16085d5ead..63f119041d 100644 --- a/src/panels/config/integrations/ha-integration-header.ts +++ b/src/panels/config/integrations/ha-integration-header.ts @@ -1,4 +1,5 @@ -import { LitElement, TemplateResult, css, html } from "lit"; +import { mdiAlertCircleOutline, mdiAlertOutline } from "@mdi/js"; +import { LitElement, TemplateResult, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/ha-svg-icon"; import { IntegrationManifest, domainToName } from "../../../data/integration"; @@ -13,6 +14,10 @@ export class HaIntegrationHeader extends LitElement { @property() public label?: string; + @property() public error?: string; + + @property() public warning?: string; + @property() public localizedDomainName?: string; @property() public domain!: string; @@ -52,8 +57,26 @@ export class HaIntegrationHeader extends LitElement { @load=${this._onImageLoad} />
-
${primary}
-
${secondary}
+
+ ${primary} +
+ ${this.error + ? html`
+ ${this + .error} +
` + : this.warning + ? html`
+ ${this + .warning} +
` + : secondary + ? html`
${secondary}
` + : nothing}
@@ -101,30 +124,52 @@ export class HaIntegrationHeader extends LitElement { flex: 1; align-self: center; } - .header .info div { + .primary, + .warning, + .error, + .secondary { word-wrap: break-word; display: -webkit-box; -webkit-box-orient: vertical; - -webkit-line-clamp: 2; overflow: hidden; text-overflow: ellipsis; } - .header-button { - display: flex; - align-items: center; - justify-content: center; - width: 36px; - } .primary { font-size: 16px; font-weight: 400; word-break: break-word; color: var(--primary-text-color); + -webkit-line-clamp: 2; + } + .hasError { + -webkit-line-clamp: 1; + font-size: 14px; + } + .warning, + .error { + line-height: 20px; + --mdc-icon-size: 20px; + -webkit-line-clamp: 1; + font-size: 0.9em; } .secondary { font-size: 14px; color: var(--secondary-text-color); } + .error ha-svg-icon { + margin-right: 4px; + color: var(--error-color); + } + .warning ha-svg-icon { + margin-right: 4px; + color: var(--warning-color); + } + .header-button { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + } `; } diff --git a/src/translations/en.json b/src/translations/en.json index 2c60787dbf..ee9351d365 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3420,7 +3420,7 @@ "loaded": "Loaded", "setup_error": "Failed to set up", "migration_error": "Migration error", - "setup_retry": "Retrying setup", + "setup_retry": "Failed setup, will retry", "not_loaded": "Not loaded", "failed_unload": "Failed to unload", "setup_in_progress": "Initializing" From a7100b96783045d7c38e8bd103866ff88a186985 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 18:33:59 +0200 Subject: [PATCH 17/18] Bumped version to 20230629.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 421f20a4ab..7c48219044 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20230628.0" +version = "20230629.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From a09d71291b957a1a658e76693f0682c3cd83c24a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 29 Jun 2023 18:47:24 +0200 Subject: [PATCH 18/18] Update discovered/disabled/ignored integrations (#17107) --- .../integrations/ha-config-flow-card.ts | 161 ++++++++---------- .../ha-config-integrations-dashboard.ts | 88 ++++++---- .../ha-integration-action-card.ts | 128 +++++++++----- .../integrations/ha-integration-header.ts | 39 +---- src/translations/en.json | 1 + 5 files changed, 216 insertions(+), 201 deletions(-) diff --git a/src/panels/config/integrations/ha-config-flow-card.ts b/src/panels/config/integrations/ha-config-flow-card.ts index 8509ab2e5f..77be858ca8 100644 --- a/src/panels/config/integrations/ha-config-flow-card.ts +++ b/src/panels/config/integrations/ha-config-flow-card.ts @@ -1,15 +1,10 @@ +import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item"; +import { mdiBookshelf, mdiCog, mdiDotsVertical, mdiOpenInNew } from "@mdi/js"; import { css, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { - mdiBookshelf, - mdiCog, - mdiDotsVertical, - mdiEyeOff, - mdiOpenInNew, -} from "@mdi/js"; -import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item"; import { fireEvent } from "../../../common/dom/fire_event"; +import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event"; import { ATTENTION_SOURCES, DISCOVERY_SOURCES, @@ -17,13 +12,12 @@ import { localizeConfigFlowTitle, } from "../../../data/config_flow"; import type { IntegrationManifest } from "../../../data/integration"; -import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event"; import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../types"; +import { documentationUrl } from "../../../util/documentation-url"; import type { DataEntryFlowProgressExtended } from "./ha-config-integrations"; import "./ha-integration-action-card"; -import { documentationUrl } from "../../../util/documentation-url"; @customElement("ha-config-flow-card") export class HaConfigFlowCard extends LitElement { @@ -38,16 +32,10 @@ export class HaConfigFlowCard extends LitElement { return html` @@ -60,72 +48,75 @@ export class HaConfigFlowCard extends LitElement { }` )} > - - - ${this.flow.context.configuration_url - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.open_configuration_url" - )} - - - - ` - : ""} - ${this.manifest - ? html`` + : ""} + ${this.flow.context.configuration_url || this.manifest + ? html` + + ${this.flow.context.configuration_url + ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.documentation" - )} - - - - ` - : ""} - ${DISCOVERY_SOURCES.includes(this.flow.context.source) && - this.flow.context.unique_id - ? html` - - ${this.hass.localize( - "ui.panel.config.integrations.ignore.ignore" - )} - - - ` - : ""} - + ? "_self" + : "_blank"} + > + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.open_configuration_url" + )} + + + + ` + : ""} + ${this.manifest + ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.documentation" + )} + + + + ` + : ""} + ` + : ""} `; } @@ -174,14 +165,6 @@ export class HaConfigFlowCard extends LitElement { } static styles = css` - .attention { - --state-color: var(--error-color); - --text-on-state-color: var(--text-primary-color); - } - .discovered { - --state-color: var(--primary-color); - --text-on-state-color: var(--text-primary-color); - } a { text-decoration: none; color: var(--primary-color); diff --git a/src/panels/config/integrations/ha-config-integrations-dashboard.ts b/src/panels/config/integrations/ha-config-integrations-dashboard.ts index 9a39a25eab..969c44bbbf 100644 --- a/src/panels/config/integrations/ha-config-integrations-dashboard.ts +++ b/src/panels/config/integrations/ha-config-integrations-dashboard.ts @@ -377,46 +377,57 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
`} ${this._showIgnored - ? html`
- ${ignoredConfigEntries.map( - (entry: ConfigEntryExtended) => html` - - ` - )} -
` + ? html`

+ ${this.hass.localize( + "ui.panel.config.integrations.ignore.ignored" + )} +

+
+ ${ignoredConfigEntries.map( + (entry: ConfigEntryExtended) => html` + + ` + )} +
` : ""} ${configEntriesInProgress.length - ? html`
- ${configEntriesInProgress.map( - (flow: DataEntryFlowProgressExtended) => html` - - ` - )} -
` + ? html`

+ ${this.hass.localize("ui.panel.config.integrations.discovered")} +

+
+ ${configEntriesInProgress.map( + (flow: DataEntryFlowProgressExtended) => html` + + ` + )} +
` : ""} ${this._showDisabled - ? html`
- ${disabledConfigEntries.map( - (entry: ConfigEntryExtended) => html` - - ` - )} -
` + ? html`

+ ${this.hass.localize("ui.panel.config.integrations.disabled")} +

+
+ ${disabledConfigEntries.map( + (entry: ConfigEntryExtended) => html` + + ` + )} +
` : ""}
${integrations.length @@ -756,6 +767,8 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { .empty-message { margin: auto; text-align: center; + grid-column-start: 1; + grid-column-end: -1; } .empty-message h1 { margin-bottom: 0; @@ -851,6 +864,9 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { .menu-badge-container { position: relative; } + h1 { + margin: 8px 0 0 16px; + } ha-button-menu { color: var(--primary-text-color); } diff --git a/src/panels/config/integrations/ha-integration-action-card.ts b/src/panels/config/integrations/ha-integration-action-card.ts index bd81203095..0d708df2ef 100644 --- a/src/panels/config/integrations/ha-integration-action-card.ts +++ b/src/panels/config/integrations/ha-integration-action-card.ts @@ -1,9 +1,14 @@ import { css, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; -import type { IntegrationManifest } from "../../../data/integration"; +import { + domainToName, + type IntegrationManifest, +} from "../../../data/integration"; import type { HomeAssistant } from "../../../types"; import "./ha-integration-header"; import "../../../components/ha-card"; +import { brandsUrl } from "../../../util/brands-url"; +import { haStyle } from "../../../resources/styles"; @customElement("ha-integration-action-card") export class HaIntegrationActionCard extends LitElement { @@ -22,49 +27,94 @@ export class HaIntegrationActionCard extends LitElement { protected render(): TemplateResult { return html` - - - +
+ +

${this.label}

+

+ ${this.localizedDomainName || + domainToName(this.hass.localize, this.domain, this.manifest)} +

+
-
+
+
`; } - static styles = css` - ha-card { - display: flex; - flex-direction: column; - height: 100%; - --ha-card-border-color: var(--state-color); - --mdc-theme-primary: var(--state-color); - } - .filler { - flex: 1; - } - .attention { - --state-color: var(--error-color); - --text-on-state-color: var(--text-primary-color); - } - .discovered { - --state-color: var(--primary-color); - --text-on-state-color: var(--text-primary-color); - } - .actions { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 6px 0; - height: 48px; - } - `; + private _onImageLoad(ev) { + ev.target.style.visibility = "initial"; + } + + private _onImageError(ev) { + ev.target.style.visibility = "hidden"; + } + + static styles = [ + haStyle, + css` + ha-card { + display: flex; + flex-direction: column; + height: 100%; + } + img { + width: 40px; + height: 40px; + } + h2 { + font-size: 16px; + font-weight: 400; + margin-top: 8px; + margin-bottom: 0; + } + h3 { + font-size: 14px; + margin: 0; + } + .header-button { + position: absolute; + top: 8px; + right: 8px; + } + .filler { + flex: 1; + } + .attention { + --state-color: var(--error-color); + --text-on-state-color: var(--text-primary-color); + } + .card-content { + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + } + .card-actions { + border-top: none; + padding-top: 0; + padding-bottom: 16px; + justify-content: center; + display: flex; + } + :host ::slotted(*) { + margin-right: 8px; + } + :host ::slotted(:last-child) { + margin-right: 0; + } + `, + ]; } declare global { diff --git a/src/panels/config/integrations/ha-integration-header.ts b/src/panels/config/integrations/ha-integration-header.ts index 63f119041d..44fc4497be 100644 --- a/src/panels/config/integrations/ha-integration-header.ts +++ b/src/panels/config/integrations/ha-integration-header.ts @@ -10,10 +10,6 @@ import { brandsUrl } from "../../../util/brands-url"; export class HaIntegrationHeader extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property() public banner?: string; - - @property() public label?: string; - @property() public error?: string; @property() public warning?: string; @@ -25,25 +21,11 @@ export class HaIntegrationHeader extends LitElement { @property({ attribute: false }) public manifest?: IntegrationManifest; protected render(): TemplateResult { - let primary: string; - let secondary: string | undefined; - const domainName = this.localizedDomainName || domainToName(this.hass.localize, this.domain, this.manifest); - if (this.label) { - primary = this.label; - secondary = - primary.toLowerCase() === domainName.toLowerCase() - ? undefined - : domainName; - } else { - primary = domainName; - } - return html` - ${!this.banner ? "" : html``}
- ${primary} + ${domainName}
${this.error ? html`
@@ -74,8 +56,6 @@ export class HaIntegrationHeader extends LitElement { ${this .warning}
` - : secondary - ? html`
${secondary}
` : nothing}
@@ -94,16 +74,6 @@ export class HaIntegrationHeader extends LitElement { } static styles = css` - .banner { - background-color: var(--state-color); - color: var(--text-on-state-color); - text-align: center; - padding: 2px; - - /* Padding is subtracted for nested elements with border radiuses */ - border-top-left-radius: calc(var(--ha-card-border-radius, 12px) - 2px); - border-top-right-radius: calc(var(--ha-card-border-radius, 12px) - 2px); - } .header { display: flex; position: relative; @@ -126,8 +96,7 @@ export class HaIntegrationHeader extends LitElement { } .primary, .warning, - .error, - .secondary { + .error { word-wrap: break-word; display: -webkit-box; -webkit-box-orient: vertical; @@ -152,10 +121,6 @@ export class HaIntegrationHeader extends LitElement { -webkit-line-clamp: 1; font-size: 0.9em; } - .secondary { - font-size: 14px; - color: var(--secondary-text-color); - } .error ha-svg-icon { margin-right: 4px; color: var(--error-color); diff --git a/src/translations/en.json b/src/translations/en.json index ee9351d365..042ec31ac6 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3301,6 +3301,7 @@ "description": "Manage integrations with services or devices", "integration": "integration", "discovered": "Discovered", + "disabled": "Disabled", "available_integrations": "Available integrations", "new_flow": "Setup another instance of {integration}", "attention": "Attention required",