From b57bc8cd0692ed0a561e790443bc35d574d4442c Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 7 Feb 2024 15:24:14 +0100 Subject: [PATCH 001/116] Matter cleanup on close dialog (#19714) --- .../integration-panels/matter/dialog-matter-ping-node.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts b/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts index 738523d16d..63aec257bf 100644 --- a/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts +++ b/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts @@ -146,6 +146,7 @@ class DialogMatterPingNode extends LitElement { public closeDialog(): void { this.device_id = undefined; this._status = undefined; + this._pingResult = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } From 95136993322cb55938a69613bc5ad9918fc3d1da Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 7 Feb 2024 15:51:01 +0100 Subject: [PATCH 002/116] Use gap instead of margin for vertical and horizontal card (#19699) --- .../lovelace/cards/hui-horizontal-stack-card.ts | 17 +---------------- .../lovelace/cards/hui-vertical-stack-card.ts | 13 +------------ 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/src/panels/lovelace/cards/hui-horizontal-stack-card.ts b/src/panels/lovelace/cards/hui-horizontal-stack-card.ts index 83a1c9ae08..502e54db4d 100644 --- a/src/panels/lovelace/cards/hui-horizontal-stack-card.ts +++ b/src/panels/lovelace/cards/hui-horizontal-stack-card.ts @@ -28,27 +28,12 @@ export class HuiHorizontalStackCard extends HuiStackCard { #root { display: flex; height: 100%; + gap: var(--horizontal-stack-card-gap, var(--stack-card-gap, 8px)); } #root > * { flex: 1 1 0; - margin: var( - --horizontal-stack-card-margin, - var(--stack-card-margin, 0 4px) - ); min-width: 0; } - #root[dir="ltr"] > *:first-child { - margin-left: 0; - } - #root[dir="ltr"] > *:last-child { - margin-right: 0; - } - #root[dir="rtl"] > *:first-child { - margin-right: 0; - } - #root[dir="rtl"] > *:last-child { - margin-left: 0; - } `, ]; } diff --git a/src/panels/lovelace/cards/hui-vertical-stack-card.ts b/src/panels/lovelace/cards/hui-vertical-stack-card.ts index e4915feea8..f03b4524d4 100644 --- a/src/panels/lovelace/cards/hui-vertical-stack-card.ts +++ b/src/panels/lovelace/cards/hui-vertical-stack-card.ts @@ -27,18 +27,7 @@ class HuiVerticalStackCard extends HuiStackCard { display: flex; flex-direction: column; height: 100%; - } - #root > * { - margin: var( - --vertical-stack-card-margin, - var(--stack-card-margin, 4px 0) - ); - } - #root > *:first-child { - margin-top: 0; - } - #root > *:last-child { - margin-bottom: 0; + gap: var(--vertical-stack-card-gap, var(--stack-card-gap, 8px)); } `, ]; From e51c98e1a779514390bd3d322bdf3d7e2f84a0c2 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 7 Feb 2024 15:52:04 +0100 Subject: [PATCH 003/116] Improve matter ping dialog (#19715) --- .../matter/dialog-matter-ping-node.ts | 64 ++++++++----------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts b/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts index 63aec257bf..77ec91ee24 100644 --- a/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts +++ b/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts @@ -1,12 +1,12 @@ import "@material/mwc-button/mwc-button"; -import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js"; +import { mdiAlertCircle, mdiCheckCircle, mdiCloseCircle } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { fireEvent } from "../../../../../common/dom/fire_event"; import "../../../../../components/ha-circular-progress"; import { createCloseHeading } from "../../../../../components/ha-dialog"; import { pingMatterNode, MatterPingResult } from "../../../../../data/matter"; -import { haStyleDialog } from "../../../../../resources/styles"; +import { haStyle, haStyleDialog } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; import { MatterPingNodeDialogParams } from "./show-dialog-matter-ping-node"; @@ -40,33 +40,24 @@ class DialogMatterPingNode extends LitElement { > ${this._pingResult ? html` -
- -
-

- ${this.hass.localize( - "ui.panel.config.matter.ping_node.ping_complete" - )} -

-
-
-
- - ${Object.entries(this._pingResult).map( - ([ip, success]) => - html`${ip} - - ` - )} - -
+

+ ${this.hass.localize( + "ui.panel.config.matter.ping_node.ping_complete" + )} +

+ + ${Object.entries(this._pingResult).map( + ([ip, success]) => + html`${ip} + + ` + )} + ${this.hass.localize("ui.common.close")} @@ -146,12 +137,12 @@ class DialogMatterPingNode extends LitElement { public closeDialog(): void { this.device_id = undefined; this._status = undefined; - this._pingResult = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } static get styles(): CSSResultGroup { return [ + haStyle, haStyleDialog, css` .success { @@ -171,23 +162,22 @@ class DialogMatterPingNode extends LitElement { margin-top: 16px; } - .stage ha-svg-icon { - width: 16px; - height: 16px; - } .stage { padding: 8px; } - ha-svg-icon { - width: 68px; - height: 48px; + mwc-list { + --mdc-list-side-padding: 0; } .flex-container ha-circular-progress, .flex-container ha-svg-icon { margin-right: 20px; } + .flex-container ha-svg-icon { + width: 68px; + height: 48px; + } `, ]; } From abb014745afa7da3e2c0a8b405f479b7999b0300 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 7 Feb 2024 19:42:39 +0100 Subject: [PATCH 004/116] Show icon of disabled entities (#19717) --- .../entity-registry-settings-editor.ts | 15 +++++----- .../config/entities/ha-config-entities.ts | 28 +++++++++++++------ 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/panels/config/entities/entity-registry-settings-editor.ts b/src/panels/config/entities/entity-registry-settings-editor.ts index 41b70f4875..03102930af 100644 --- a/src/panels/config/entities/entity-registry-settings-editor.ts +++ b/src/panels/config/entities/entity-registry-settings-editor.ts @@ -31,11 +31,11 @@ import "../../../components/ha-area-picker"; import "../../../components/ha-icon"; import "../../../components/ha-icon-button-next"; import "../../../components/ha-icon-picker"; -import "../../../components/ha-state-icon"; import "../../../components/ha-list-item"; import "../../../components/ha-radio"; import "../../../components/ha-select"; import "../../../components/ha-settings-row"; +import "../../../components/ha-state-icon"; import "../../../components/ha-switch"; import type { HaSwitch } from "../../../components/ha-switch"; import "../../../components/ha-textfield"; @@ -52,10 +52,6 @@ import { createConfigFlow, handleConfigFlowStep, } from "../../../data/config_flow"; -import { - createOptionsFlow, - handleOptionsFlowStep, -} from "../../../data/options_flow"; import { DataEntryFlowStepCreateEntry } from "../../../data/data_entry_flow"; import { DeviceRegistryEntry, @@ -70,9 +66,13 @@ import { subscribeEntityRegistry, updateEntityRegistryEntry, } from "../../../data/entity_registry"; -import { entityIcon } from "../../../data/icons"; +import { entityIcon, entryIcon } from "../../../data/icons"; import { domainToName } from "../../../data/integration"; import { getNumberDeviceClassConvertibleUnits } from "../../../data/number"; +import { + createOptionsFlow, + handleOptionsFlowStep, +} from "../../../data/options_flow"; import { getSensorDeviceClassConvertibleUnits, getSensorNumericDeviceClasses, @@ -392,7 +392,8 @@ export class EntityRegistrySettingsEditor extends LitElement { )} .placeholder=${this.entry.original_icon || stateObj?.attributes.icon || - (stateObj && until(entityIcon(this.hass, stateObj)))} + (stateObj && until(entityIcon(this.hass, stateObj))) || + until(entryIcon(this.hass, this.entry))} .disabled=${this.disabled} > ${!this._icon && !stateObj?.attributes.icon && stateObj diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 0599d1e75e..6ca9b07bcc 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -25,6 +25,7 @@ import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; +import { until } from "lit/directives/until"; import memoize from "memoize-one"; import type { HASSDomEvent } from "../../../common/dom/fire_event"; import { computeDomain } from "../../../common/entity/compute_domain"; @@ -42,6 +43,7 @@ import type { } from "../../../components/data-table/ha-data-table"; import "../../../components/ha-button-menu"; import "../../../components/ha-check-list-item"; +import "../../../components/ha-icon"; import "../../../components/ha-icon-button"; import "../../../components/ha-svg-icon"; import { ConfigEntry, getConfigEntries } from "../../../data/config_entries"; @@ -53,6 +55,7 @@ import { removeEntityRegistryEntry, updateEntityRegistryEntry, } from "../../../data/entity_registry"; +import { entryIcon } from "../../../data/icons"; import { domainToName } from "../../../data/integration"; import { showAlertDialog, @@ -207,14 +210,23 @@ export class HaConfigEntities extends LitElement { title: "", label: localize("ui.panel.config.entities.picker.headers.state_icon"), type: "icon", - template: (entry) => html` - - `, + template: (entry) => + entry.icon + ? html` + + ` + : html` + + `, }, name: { main: true, From b1e1b44c75127f9ba1c34e1193dd74fbf7da9a6b Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 8 Feb 2024 12:04:23 +0100 Subject: [PATCH 005/116] Use rgb theme variables for qrcode (#19726) --- src/components/ha-qr-code.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/components/ha-qr-code.ts b/src/components/ha-qr-code.ts index 47190c180c..ce9109e1ed 100644 --- a/src/components/ha-qr-code.ts +++ b/src/components/ha-qr-code.ts @@ -2,6 +2,7 @@ import { LitElement, PropertyValues, css, html, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import QRCode from "qrcode"; import "./ha-alert"; +import { rgb2hex } from "../common/color/convert-color"; @customElement("ha-qr-code") export class HaQrCode extends LitElement { @@ -65,6 +66,26 @@ export class HaQrCode extends LitElement { changedProperties.has("centerImage")) ) { const computedStyles = getComputedStyle(this); + const textRgb = computedStyles.getPropertyValue( + "--rgb-primary-text-color" + ); + const backgroundRgb = computedStyles.getPropertyValue( + "--rgb-card-background-color" + ); + const textHex = rgb2hex( + textRgb.split(",").map((a) => parseInt(a, 10)) as [ + number, + number, + number, + ] + ); + const backgroundHex = rgb2hex( + backgroundRgb.split(",").map((a) => parseInt(a, 10)) as [ + number, + number, + number, + ] + ); QRCode.toCanvas(canvas, this.data, { errorCorrectionLevel: @@ -74,8 +95,8 @@ export class HaQrCode extends LitElement { margin: this.margin, maskPattern: this.maskPattern, color: { - light: computedStyles.getPropertyValue("--card-background-color"), - dark: computedStyles.getPropertyValue("--primary-text-color"), + light: backgroundHex, + dark: textHex, }, }).catch((err) => { this._error = err.message; From 23cbecb2c4e6caf46dfcffefc65231679bbe1d15 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 8 Feb 2024 13:33:10 +0100 Subject: [PATCH 006/116] Add service icons to traces (again :D) (#19728) * Add service icons to traces (again :D) * Update hat-graph-node.ts --- .../src/pages/automation/trace-timeline.ts | 1 - src/components/trace/hat-graph-node.ts | 17 ++++++++++---- src/components/trace/hat-script-graph.ts | 22 ++++++++++++++----- .../config/automation/ha-automation-trace.ts | 1 + src/panels/config/script/ha-script-trace.ts | 1 + 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/gallery/src/pages/automation/trace-timeline.ts b/gallery/src/pages/automation/trace-timeline.ts index 638da986e0..a9a0e1a9d5 100644 --- a/gallery/src/pages/automation/trace-timeline.ts +++ b/gallery/src/pages/automation/trace-timeline.ts @@ -3,7 +3,6 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import "../../../../src/components/ha-card"; -import "../../../../src/components/trace/hat-script-graph"; import "../../../../src/components/trace/hat-trace-timeline"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import { HomeAssistant } from "../../../../src/types"; diff --git a/src/components/trace/hat-graph-node.ts b/src/components/trace/hat-graph-node.ts index b9b6a499f0..4da3f7b7c3 100644 --- a/src/components/trace/hat-graph-node.ts +++ b/src/components/trace/hat-graph-node.ts @@ -5,6 +5,7 @@ import { html, TemplateResult, svg, + nothing, } from "lit"; import { customElement, property } from "lit/decorators"; import { NODE_SIZE, SPACING } from "./hat-graph-const"; @@ -51,7 +52,7 @@ export class HatGraphNode extends LitElement { : Math.ceil((NODE_SIZE + SPACING * 2) / 2)} ${width} ${height}" > ${this.graphStart - ? `` + ? nothing : svg` - } ${this.badge ? svg` @@ -81,9 +81,11 @@ export class HatGraphNode extends LitElement { >${this.badge > 9 ? "9+" : this.badge} ` - : ""} + : nothing} - ${this.iconPath ? svg`` : ""} + ${this.iconPath + ? svg`` + : svg``} @@ -152,6 +154,13 @@ export class HatGraphNode extends LitElement { path.icon { fill: var(--icon-clr); } + foreignObject { + width: 24px; + height: 24px; + } + .icon { + color: var(--icon-clr); + } `; } } diff --git a/src/components/trace/hat-script-graph.ts b/src/components/trace/hat-script-graph.ts index 29aad80564..7bc0cbc0d9 100644 --- a/src/components/trace/hat-script-graph.ts +++ b/src/components/trace/hat-script-graph.ts @@ -17,11 +17,10 @@ import { mdiRoomService, mdiShuffleDisabled, } from "@mdi/js"; -import { LitElement, PropertyValues, css, html } from "lit"; +import { LitElement, PropertyValues, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { ensureArray } from "../../common/array/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; -import { ACTION_ICONS } from "../../data/action"; import { Condition, Trigger } from "../../data/automation"; import { Action, @@ -41,11 +40,14 @@ import { IfActionTraceStep, TraceExtended, } from "../../data/trace"; +import { HomeAssistant } from "../../types"; import "../ha-icon-button"; +import "../ha-service-icon"; import "./hat-graph-branch"; import { BRANCH_HEIGHT, NODE_SIZE, SPACING } from "./hat-graph-const"; import "./hat-graph-node"; import "./hat-graph-spacer"; +import { ACTION_ICONS } from "../../data/action"; export interface NodeInfo { path: string; @@ -64,6 +66,8 @@ export class HatScriptGraph extends LitElement { @property({ attribute: false }) public selected?: string; + public hass!: HomeAssistant; + public renderedNodes: Record = {}; public trackedNodes: Record = {}; @@ -415,13 +419,21 @@ export class HatScriptGraph extends LitElement { return html` + > + ${node.service + ? html`` + : nothing} + `; } @@ -667,8 +679,6 @@ export class HatScriptGraph extends LitElement { } .parent { margin-left: 8px; - margin-inline-start: 8px; - margin-inline-end: initial; margin-top: 16px; } .error { diff --git a/src/panels/config/automation/ha-automation-trace.ts b/src/panels/config/automation/ha-automation-trace.ts index c8188bdcaa..c752e562ed 100644 --- a/src/panels/config/automation/ha-automation-trace.ts +++ b/src/panels/config/automation/ha-automation-trace.ts @@ -232,6 +232,7 @@ export class HaAutomationTrace extends LitElement {
Date: Thu, 8 Feb 2024 14:33:34 +0100 Subject: [PATCH 007/116] Don't use ES5 build for dev server (#19731) --- build-scripts/gulp/webpack.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/build-scripts/gulp/webpack.js b/build-scripts/gulp/webpack.js index 6365ef3104..f55d77f345 100644 --- a/build-scripts/gulp/webpack.js +++ b/build-scripts/gulp/webpack.js @@ -115,7 +115,9 @@ gulp.task("webpack-prod-app", () => gulp.task("webpack-dev-server-demo", () => runDevServer({ - compiler: webpack(bothBuilds(createDemoConfig, { isProdBuild: false })), + compiler: webpack( + createDemoConfig({ isProdBuild: false, latestBuild: true }) + ), contentBase: paths.demo_output_root, port: 8090, }) @@ -131,7 +133,9 @@ gulp.task("webpack-prod-demo", () => gulp.task("webpack-dev-server-cast", () => runDevServer({ - compiler: webpack(bothBuilds(createCastConfig, { isProdBuild: false })), + compiler: webpack( + createCastConfig({ isProdBuild: false, latestBuild: true }) + ), contentBase: paths.cast_output_root, port: 8080, // Accessible from the network, because that's how Cast hits it. @@ -174,8 +178,9 @@ gulp.task("webpack-prod-hassio", () => gulp.task("webpack-dev-server-gallery", () => runDevServer({ - // We don't use the es5 build, but the dev server will fuck up the publicPath if we don't - compiler: webpack(bothBuilds(createGalleryConfig, { isProdBuild: false })), + compiler: webpack( + createGalleryConfig({ isProdBuild: false, latestBuild: true }) + ), contentBase: paths.gallery_output_root, port: 8100, listenHost: "0.0.0.0", From 6033f8b31a3ae8e5c12ac003bd4f4c80306d8cab Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 8 Feb 2024 14:38:08 +0100 Subject: [PATCH 008/116] Add QR and constant selector to gallery (#19730) * Add QR and constant selector to gallery * Update ha-selector.ts --- gallery/src/components/demo-black-white-row.ts | 8 +++++--- gallery/src/pages/components/ha-selector.ts | 14 +++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/gallery/src/components/demo-black-white-row.ts b/gallery/src/components/demo-black-white-row.ts index 30b2c533b7..851cb5bb71 100644 --- a/gallery/src/components/demo-black-white-row.ts +++ b/gallery/src/components/demo-black-white-row.ts @@ -1,5 +1,5 @@ import { Button } from "@material/mwc-button"; -import { html, LitElement, css, TemplateResult } from "lit"; +import { html, LitElement, css, TemplateResult, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element"; import { fireEvent } from "../../../src/common/dom/fire_event"; @@ -9,7 +9,7 @@ import "../../../src/components/ha-card"; class DemoBlackWhiteRow extends LitElement { @property() title!: string; - @property() value!: any; + @property() value?: any; @property({ type: Boolean }) public disabled = false; @@ -45,7 +45,9 @@ class DemoBlackWhiteRow extends LitElement {
-
${JSON.stringify(this.value, undefined, 2)}
+ ${this.value + ? html`
${JSON.stringify(this.value, undefined, 2)}
` + : nothing}
`; diff --git a/gallery/src/pages/components/ha-selector.ts b/gallery/src/pages/components/ha-selector.ts index 78dbe681bb..fceab71c29 100644 --- a/gallery/src/pages/components/ha-selector.ts +++ b/gallery/src/pages/components/ha-selector.ts @@ -275,6 +275,14 @@ const SCHEMAS: { selector: { color_temp: {} }, }, color_rgb: { name: "Color", selector: { color_rgb: {} } }, + qr_code: { + name: "QR Code", + selector: { qr_code: { data: "https://home-assistant.io" } }, + }, + constant: { + name: "Constant", + selector: { constant: { value: true, label: "Yes!" } }, + }, }, }, { @@ -501,7 +509,7 @@ class DemoHaSelector extends LitElement implements ProvideHassElement { this.requestUpdate(); }; return html` - + ${["light", "dark"].map((slot) => Object.entries(info.input).map( ([key, value]) => html` @@ -534,8 +542,8 @@ class DemoHaSelector extends LitElement implements ProvideHassElement { } static styles = css` - ha-selector { - width: 60; + ha-settings-row { + --paper-item-body-two-line-min-height: 0; } .options { max-width: 800px; From 1e35f973d670f4cec8f8287dd3d0fc1598ef1625 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 8 Feb 2024 15:38:55 +0100 Subject: [PATCH 009/116] Fix suggest card dialog (#19735) --- .../lovelace/editor/card-editor/hui-dialog-create-card.ts | 7 +++++++ .../editor/card-editor/show-suggest-card-dialog.ts | 8 ++++---- .../editor/unused-entities/hui-unused-entities.ts | 8 ++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts index 1752a24174..4ce93735d4 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -21,6 +21,7 @@ import "./hui-entity-picker-table"; import { CreateCardDialogParams } from "./show-create-card-dialog"; import { showEditCardDialog } from "./show-edit-card-dialog"; import { showSuggestCardDialog } from "./show-suggest-card-dialog"; +import { computeCards } from "../../common/generate-lovelace-config"; declare global { interface HASSDomEvents { @@ -242,11 +243,17 @@ export class HuiCreateDialogCard } private _suggestCards(): void { + const cardConfig = computeCards( + this.hass.states, + this._selectedEntities, + {} + ); showSuggestCardDialog(this, { lovelaceConfig: this._params!.lovelaceConfig, saveConfig: this._params!.saveConfig, path: this._params!.path as [number], entities: this._selectedEntities, + cardConfig, }); this.closeDialog(); diff --git a/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts index de8021649d..ea43443d06 100644 --- a/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts @@ -7,11 +7,11 @@ export interface SuggestCardDialogParams { yaml?: boolean; saveConfig?: (config: LovelaceConfig) => void; path?: [number]; - entities?: string[]; // Entities used to generate the card config. We pass this to create dialog when user chooses "Pick own" - cardConfig?: LovelaceCardConfig[]; // We can pass a suggested config + entities?: string[]; // We pass this to create dialog when user chooses "Pick own" + cardConfig: LovelaceCardConfig[]; // We can pass a suggested config } -const importsuggestCardDialog = () => import("./hui-dialog-suggest-card"); +const importSuggestCardDialog = () => import("./hui-dialog-suggest-card"); export const showSuggestCardDialog = ( element: HTMLElement, @@ -19,7 +19,7 @@ export const showSuggestCardDialog = ( ): void => { fireEvent(element, "show-dialog", { dialogTag: "hui-dialog-suggest-card", - dialogImport: importsuggestCardDialog, + dialogImport: importSuggestCardDialog, dialogParams: suggestCardDialogParams, }); }; diff --git a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts index 18ce15b683..98cd3f3163 100644 --- a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts +++ b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts @@ -21,6 +21,7 @@ import "../card-editor/hui-entity-picker-table"; import { showSuggestCardDialog } from "../card-editor/show-suggest-card-dialog"; import { showSelectViewDialog } from "../select-view/show-select-view-dialog"; import { LovelaceConfig } from "../../../../data/lovelace/config/types"; +import { computeCards } from "../../common/generate-lovelace-config"; @customElement("hui-unused-entities") export class HuiUnusedEntities extends LitElement { @@ -126,12 +127,18 @@ export class HuiUnusedEntities extends LitElement { } private _addToLovelaceView(): void { + const cardConfig = computeCards( + this.hass.states, + this._selectedEntities, + {} + ); if (this.lovelace.config.views.length === 1) { showSuggestCardDialog(this, { lovelaceConfig: this.lovelace.config!, saveConfig: this.lovelace.saveConfig, path: [0], entities: this._selectedEntities, + cardConfig, }); return; } @@ -144,6 +151,7 @@ export class HuiUnusedEntities extends LitElement { saveConfig: this.lovelace.saveConfig, path: [viewIndex], entities: this._selectedEntities, + cardConfig, }); }, }); From 189793bff40a4f0b0193e36eb745f8056b131dc7 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 8 Feb 2024 15:40:03 +0100 Subject: [PATCH 010/116] Fix demo dashboard (#19734) --- demo/src/ha-demo.ts | 2 + demo/src/stubs/sensor.ts | 58 +++++++++++++++++++ demo/src/stubs/todo.ts | 1 + .../hui-input-select-entity-row.ts | 16 +++-- 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 demo/src/stubs/sensor.ts diff --git a/demo/src/ha-demo.ts b/demo/src/ha-demo.ts index 17550a6af6..0a9b131556 100644 --- a/demo/src/ha-demo.ts +++ b/demo/src/ha-demo.ts @@ -23,6 +23,7 @@ import { mockMediaPlayer } from "./stubs/media_player"; import { mockPersistentNotification } from "./stubs/persistent_notification"; import { mockRecorder } from "./stubs/recorder"; import { mockTodo } from "./stubs/todo"; +import { mockSensor } from "./stubs/sensor"; import { mockSystemLog } from "./stubs/system_log"; import { mockTemplate } from "./stubs/template"; import { mockTranslations } from "./stubs/translations"; @@ -50,6 +51,7 @@ export class HaDemo extends HomeAssistantAppEl { mockHistory(hass); mockRecorder(hass); mockTodo(hass); + mockSensor(hass); mockSystemLog(hass); mockTemplate(hass); mockEvents(hass); diff --git a/demo/src/stubs/sensor.ts b/demo/src/stubs/sensor.ts new file mode 100644 index 0000000000..19c9f2e344 --- /dev/null +++ b/demo/src/stubs/sensor.ts @@ -0,0 +1,58 @@ +import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; + +export const mockSensor = (hass: MockHomeAssistant) => { + hass.mockWS("sensor/numeric_device_classes", () => [ + { + numeric_device_classes: [ + "volume_storage", + "gas", + "data_size", + "irradiance", + "wind_speed", + "volatile_organic_compounds", + "volatile_organic_compounds_parts", + "voltage", + "frequency", + "precipitation_intensity", + "volume", + "precipitation", + "battery", + "nitrogen_dioxide", + "speed", + "signal_strength", + "pm1", + "nitrous_oxide", + "atmospheric_pressure", + "data_rate", + "temperature", + "power_factor", + "aqi", + "current", + "volume_flow_rate", + "humidity", + "duration", + "ozone", + "distance", + "pressure", + "pm25", + "weight", + "energy", + "carbon_monoxide", + "apparent_power", + "illuminance", + "energy_storage", + "moisture", + "power", + "water", + "carbon_dioxide", + "ph", + "reactive_power", + "monetary", + "nitrogen_monoxide", + "pm10", + "sound_pressure", + "sulphur_dioxide", + ], + }, + ]); +}; diff --git a/demo/src/stubs/todo.ts b/demo/src/stubs/todo.ts index b0393f6c88..71d49cdede 100644 --- a/demo/src/stubs/todo.ts +++ b/demo/src/stubs/todo.ts @@ -21,4 +21,5 @@ export const mockTodo = (hass: MockHomeAssistant) => { }, ] as TodoItem[], })); + hass.mockWS("todo/item/subscribe", (_msg, _hass) => () => {}); }; diff --git a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts index 4dbfd6f6bf..2d4f6e5bf3 100644 --- a/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-input-select-entity-row.ts @@ -40,14 +40,20 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow { protected updated(changedProps: PropertyValues) { super.updated(changedProps); + if (!this._config) { + return; + } if (changedProps.has("hass")) { const oldHass = changedProps.get("hass"); + const stateObj = this.hass?.states[this._config.entity] as + | InputSelectEntity + | undefined; + const oldStateObj = oldHass?.states[this._config.entity] as + | InputSelectEntity + | undefined; if ( - this.hass && - oldHass && - this._config?.entity && - this.hass.states[this._config.entity].attributes.options !== - oldHass.states[this._config.entity].attributes.options + stateObj && + stateObj.attributes.options !== oldStateObj?.attributes.options ) { this._haSelect.layoutOptions(); } From 336376d2a56fec2a222dbc5a93cb8c6abfe2a677 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 8 Feb 2024 17:04:05 +0100 Subject: [PATCH 011/116] Fix icons in gallery, demo and cast (#19732) * Fix icons in gallery and demo * Add to lovelace gallery * Update icons.ts * add BACKWARDS_COMPAT support for icons * Update entity-state.ts * Update icons.ts * Update icons.ts --- demo/src/ha-demo.ts | 2 + demo/src/stubs/icons.ts | 33 + .../src/pages/lovelace/alarm-panel-card.ts | 2 + gallery/src/pages/lovelace/area-card.ts | 2 + .../src/pages/lovelace/conditional-card.ts | 2 + gallery/src/pages/lovelace/entities-card.ts | 2 + .../src/pages/lovelace/entity-button-card.ts | 2 + .../src/pages/lovelace/entity-filter-card.ts | 2 + gallery/src/pages/lovelace/gauge-card.ts | 2 + gallery/src/pages/lovelace/glance-card.ts | 2 + .../src/pages/lovelace/grid-and-stack-card.ts | 2 + gallery/src/pages/lovelace/light-card.ts | 2 + .../pages/lovelace/picture-elements-card.ts | 2 + .../src/pages/lovelace/picture-entity-card.ts | 2 + .../src/pages/lovelace/picture-glance-card.ts | 2 + gallery/src/pages/lovelace/plant-card.ts | 2 + gallery/src/pages/lovelace/thermostat-card.ts | 2 + gallery/src/pages/lovelace/tile-card.ts | 2 + gallery/src/pages/lovelace/todo-list-card.ts | 2 + gallery/src/pages/misc/entity-state.ts | 12 + src/data/icons.ts | 23 +- src/fake_data/demo_config.ts | 48 +- src/fake_data/entity_component_icons.ts | 962 ++++++++++++++++++ 23 files changed, 1109 insertions(+), 5 deletions(-) create mode 100644 demo/src/stubs/icons.ts create mode 100644 src/fake_data/entity_component_icons.ts diff --git a/demo/src/ha-demo.ts b/demo/src/ha-demo.ts index 0a9b131556..4b4d919d2b 100644 --- a/demo/src/ha-demo.ts +++ b/demo/src/ha-demo.ts @@ -17,6 +17,7 @@ import { energyEntities } from "./stubs/entities"; import { mockEntityRegistry } from "./stubs/entity_registry"; import { mockEvents } from "./stubs/events"; import { mockFrontend } from "./stubs/frontend"; +import { mockIcons } from "./stubs/icons"; import { mockHistory } from "./stubs/history"; import { mockLovelace } from "./stubs/lovelace"; import { mockMediaPlayer } from "./stubs/media_player"; @@ -57,6 +58,7 @@ export class HaDemo extends HomeAssistantAppEl { mockEvents(hass); mockMediaPlayer(hass); mockFrontend(hass); + mockIcons(hass); mockEnergy(hass); mockPersistentNotification(hass); mockConfigEntries(hass); diff --git a/demo/src/stubs/icons.ts b/demo/src/stubs/icons.ts new file mode 100644 index 0000000000..930a245d52 --- /dev/null +++ b/demo/src/stubs/icons.ts @@ -0,0 +1,33 @@ +import { IconCategory } from "../../../src/data/icons"; +import { ENTITY_COMPONENT_ICONS } from "../../../src/fake_data/entity_component_icons"; +import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; + +export const mockIcons = (hass: MockHomeAssistant) => { + hass.mockWS( + "frontend/get_icons", + async ({ + category, + integration, + }: { + category: IconCategory; + integration?: string; + }) => { + if (integration) { + try { + const response = await fetch( + `https://raw.githubusercontent.com/home-assistant/core/dev/homeassistant/components/${integration}/icons.json` + ).then((resp) => resp.json()); + return { resources: { [integration]: response[category] || {} } }; + } catch { + return { resources: {} }; + } + } + if (category === "entity_component") { + return { + resources: ENTITY_COMPONENT_ICONS, + }; + } + return { resources: {} }; + } + ); +}; diff --git a/gallery/src/pages/lovelace/alarm-panel-card.ts b/gallery/src/pages/lovelace/alarm-panel-card.ts index b55b1ca5d1..45a4c20b68 100644 --- a/gallery/src/pages/lovelace/alarm-panel-card.ts +++ b/gallery/src/pages/lovelace/alarm-panel-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("alarm_control_panel", "alarm", "disarmed", { @@ -84,6 +85,7 @@ class DemoAlarmPanelEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/area-card.ts b/gallery/src/pages/lovelace/area-card.ts index 30f98b07de..ea7afd7db9 100644 --- a/gallery/src/pages/lovelace/area-card.ts +++ b/gallery/src/pages/lovelace/area-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "bed_light", "on", { @@ -146,6 +147,7 @@ class DemoArea extends LitElement { entity_id: "binary_sensor.kitchen_door", }, ]); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/conditional-card.ts b/gallery/src/pages/lovelace/conditional-card.ts index bdaf093ed3..b59d5b3bad 100644 --- a/gallery/src/pages/lovelace/conditional-card.ts +++ b/gallery/src/pages/lovelace/conditional-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "controller_1", "on", { @@ -66,6 +67,7 @@ class DemoConditional extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/entities-card.ts b/gallery/src/pages/lovelace/entities-card.ts index aa6368878d..a7131557e6 100644 --- a/gallery/src/pages/lovelace/entities-card.ts +++ b/gallery/src/pages/lovelace/entities-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "bed_light", "on", { @@ -323,6 +324,7 @@ class DemoEntities extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/entity-button-card.ts b/gallery/src/pages/lovelace/entity-button-card.ts index a90f7c776d..daa6071571 100644 --- a/gallery/src/pages/lovelace/entity-button-card.ts +++ b/gallery/src/pages/lovelace/entity-button-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "bed_light", "on", { @@ -82,6 +83,7 @@ class DemoButtonEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/entity-filter-card.ts b/gallery/src/pages/lovelace/entity-filter-card.ts index 879cf3bb71..86b0b9cb93 100644 --- a/gallery/src/pages/lovelace/entity-filter-card.ts +++ b/gallery/src/pages/lovelace/entity-filter-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("device_tracker", "demo_paulus", "work", { @@ -123,6 +124,7 @@ class DemoEntityFilter extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/gauge-card.ts b/gallery/src/pages/lovelace/gauge-card.ts index ae99d1b1be..43d3e60d19 100644 --- a/gallery/src/pages/lovelace/gauge-card.ts +++ b/gallery/src/pages/lovelace/gauge-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("sensor", "brightness", "12", {}), @@ -128,6 +129,7 @@ class DemoGaugeEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/glance-card.ts b/gallery/src/pages/lovelace/glance-card.ts index c2a7c22ece..ab2f29d974 100644 --- a/gallery/src/pages/lovelace/glance-card.ts +++ b/gallery/src/pages/lovelace/glance-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("device_tracker", "demo_paulus", "home", { @@ -238,6 +239,7 @@ class DemoGlanceEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/grid-and-stack-card.ts b/gallery/src/pages/lovelace/grid-and-stack-card.ts index 9fc7d6f5e7..bac8a6f157 100644 --- a/gallery/src/pages/lovelace/grid-and-stack-card.ts +++ b/gallery/src/pages/lovelace/grid-and-stack-card.ts @@ -4,6 +4,7 @@ import { mockHistory } from "../../../../demo/src/stubs/history"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "kitchen_lights", "on", { @@ -214,6 +215,7 @@ class DemoStack extends LitElement { hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); mockHistory(hass); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/light-card.ts b/gallery/src/pages/lovelace/light-card.ts index 3b0fc01011..1590edce0f 100644 --- a/gallery/src/pages/lovelace/light-card.ts +++ b/gallery/src/pages/lovelace/light-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "bed_light", "on", { @@ -76,6 +77,7 @@ class DemoLightEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/picture-elements-card.ts b/gallery/src/pages/lovelace/picture-elements-card.ts index 588d6be9bf..7f6b0c99cb 100644 --- a/gallery/src/pages/lovelace/picture-elements-card.ts +++ b/gallery/src/pages/lovelace/picture-elements-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "bed_light", "on", { @@ -138,6 +139,7 @@ class DemoPictureElements extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/picture-entity-card.ts b/gallery/src/pages/lovelace/picture-entity-card.ts index b88d97cbce..1573f0dbec 100644 --- a/gallery/src/pages/lovelace/picture-entity-card.ts +++ b/gallery/src/pages/lovelace/picture-entity-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("light", "kitchen_lights", "on", { @@ -93,6 +94,7 @@ class DemoPictureEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/picture-glance-card.ts b/gallery/src/pages/lovelace/picture-glance-card.ts index f698754ea6..dccc05e09b 100644 --- a/gallery/src/pages/lovelace/picture-glance-card.ts +++ b/gallery/src/pages/lovelace/picture-glance-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("switch", "decorative_lights", "on", { @@ -134,6 +135,7 @@ class DemoPictureGlance extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/plant-card.ts b/gallery/src/pages/lovelace/plant-card.ts index 99b6780aee..6f04b474b3 100644 --- a/gallery/src/pages/lovelace/plant-card.ts +++ b/gallery/src/pages/lovelace/plant-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; import { createPlantEntities } from "../../data/plants"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const CONFIGS = [ { @@ -43,6 +44,7 @@ export class DemoPlantEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(createPlantEntities()); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/thermostat-card.ts b/gallery/src/pages/lovelace/thermostat-card.ts index 623adf5e34..1775a826e2 100644 --- a/gallery/src/pages/lovelace/thermostat-card.ts +++ b/gallery/src/pages/lovelace/thermostat-card.ts @@ -3,6 +3,7 @@ import { customElement, query } from "lit/decorators"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("climate", "ecobee", "auto", { @@ -116,6 +117,7 @@ class DemoThermostatEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/tile-card.ts b/gallery/src/pages/lovelace/tile-card.ts index 7113f4d048..62ab5abd23 100644 --- a/gallery/src/pages/lovelace/tile-card.ts +++ b/gallery/src/pages/lovelace/tile-card.ts @@ -6,6 +6,7 @@ import { VacuumEntityFeature } from "../../../../src/data/vacuum"; import { getEntity } from "../../../../src/fake_data/entity"; import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("switch", "tv_outlet", "on", { @@ -184,6 +185,7 @@ class DemoTile extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); } } diff --git a/gallery/src/pages/lovelace/todo-list-card.ts b/gallery/src/pages/lovelace/todo-list-card.ts index 49a61a61a1..b03ffd1a93 100644 --- a/gallery/src/pages/lovelace/todo-list-card.ts +++ b/gallery/src/pages/lovelace/todo-list-card.ts @@ -4,6 +4,7 @@ import { provideHass } from "../../../../src/fake_data/provide_hass"; import "../../components/demo-cards"; import { getEntity } from "../../../../src/fake_data/entity"; import { mockTodo } from "../../../../demo/src/stubs/todo"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; const ENTITIES = [ getEntity("todo", "shopping_list", "2", { @@ -47,6 +48,7 @@ class DemoTodoListEntity extends LitElement { hass.updateTranslations(null, "en"); hass.updateTranslations("lovelace", "en"); hass.addEntities(ENTITIES); + mockIcons(hass); mockTodo(hass); } diff --git a/gallery/src/pages/misc/entity-state.ts b/gallery/src/pages/misc/entity-state.ts index 4c2194c95c..e454553a4b 100644 --- a/gallery/src/pages/misc/entity-state.ts +++ b/gallery/src/pages/misc/entity-state.ts @@ -11,6 +11,7 @@ import "../../../../src/components/data-table/ha-data-table"; import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table"; import "../../../../src/components/entity/state-badge"; import { provideHass } from "../../../../src/fake_data/provide_hass"; +import { mockIcons } from "../../../../demo/src/stubs/icons"; import { HomeAssistant } from "../../../../src/types"; const SENSOR_DEVICE_CLASSES = [ @@ -291,6 +292,7 @@ const ENTITIES: HassEntity[] = [ createEntity("water_heater.high_demand", "high_demand"), createEntity("water_heater.heat_pump", "heat_pump"), createEntity("water_heater.gas", "gas"), + createEntity("select.speed", "ridiculous_speed"), ]; function createEntity( @@ -397,6 +399,16 @@ export class DemoEntityState extends LitElement { protected firstUpdated(changedProps) { super.firstUpdated(changedProps); const hass = provideHass(this); + mockIcons(hass); + hass.updateHass({ + entities: { + "select.speed": { + entity_id: "select.speed", + translation_key: "speed", + platform: "demo", + }, + }, + }); hass.updateTranslations(null, "en"); hass.updateTranslations("config", "en"); } diff --git a/src/data/icons.ts b/src/data/icons.ts index dbb1a16515..dcb3952f55 100644 --- a/src/data/icons.ts +++ b/src/data/icons.ts @@ -9,6 +9,7 @@ import { EntityRegistryEntry, } from "./entity_registry"; import { isComponentLoaded } from "../common/config/is_component_loaded"; +import { atLeastVersion } from "../common/config/version"; const resources: { entity: Record>; @@ -46,10 +47,10 @@ interface PlatformIcons { }; } -interface ComponentIcons { +export interface ComponentIcons { [device_class: string]: { - state: Record; - state_attributes: Record< + state?: Record; + state_attributes?: Record< string, { state: Record; @@ -91,7 +92,10 @@ export const getPlatformIcons = async ( if (!force && integration in resources.entity) { return resources.entity[integration]; } - if (!isComponentLoaded(hass, integration)) { + if ( + !isComponentLoaded(hass, integration) || + !atLeastVersion(hass.connection.haVersion, 2024, 2) + ) { return undefined; } const result = getHassIcons(hass, "entity", integration).then( @@ -106,6 +110,16 @@ export const getComponentIcons = async ( domain: string, force = false ): Promise => { + // For Cast, old instances can connect to it. + if ( + __BACKWARDS_COMPAT__ && + !atLeastVersion(hass.connection.haVersion, 2024, 2) + ) { + return import("../fake_data/entity_component_icons") + .then((mod) => mod.ENTITY_COMPONENT_ICONS) + .then((res) => res[domain]); + } + if ( !force && resources.entity_component.resources && @@ -113,6 +127,7 @@ export const getComponentIcons = async ( ) { return resources.entity_component.resources.then((res) => res[domain]); } + if (!isComponentLoaded(hass, domain)) { return undefined; } diff --git a/src/fake_data/demo_config.ts b/src/fake_data/demo_config.ts index caaf1bb558..2d6a6fda15 100644 --- a/src/fake_data/demo_config.ts +++ b/src/fake_data/demo_config.ts @@ -14,7 +14,53 @@ export const demoConfig: HassConfig = { wind_speed: "m/s", accumulated_precipitation: "mm", }, - components: ["notify.html5", "history", "todo", "forecast_solar", "energy"], + components: [ + "notify.html5", + "history", + "forecast_solar", + "energy", + "person", + "number", + "select", + "tts", + "datetime", + "vacuum", + "wake_word", + "light", + "alarm_control_panel", + "text", + "lawn_mower", + "siren", + "input_boolean", + "lock", + "calendar", + "image", + "device_tracker", + "scene", + "script", + "todo", + "cover", + "switch", + "button", + "water_heater", + "binary_sensor", + "sensor", + "humidifier", + "valve", + "time", + "media_player", + "air_quality", + "camera", + "date", + "fan", + "automation", + "weather", + "climate", + "stt", + "update", + "event", + "demo", + ], time_zone: "America/Los_Angeles", config_dir: "/config", version: "DEMO", diff --git a/src/fake_data/entity_component_icons.ts b/src/fake_data/entity_component_icons.ts new file mode 100644 index 0000000000..595c01cd0f --- /dev/null +++ b/src/fake_data/entity_component_icons.ts @@ -0,0 +1,962 @@ +import { ComponentIcons } from "../data/icons"; + +export const ENTITY_COMPONENT_ICONS: Record = { + person: { + _: { + default: "mdi:account", + state: { + not_home: "mdi:account-arrow-right", + }, + }, + }, + number: { + _: { + default: "mdi:ray-vertex", + }, + apparent_power: { + default: "mdi:flash", + }, + aqi: { + default: "mdi:air-filter", + }, + atmospheric_pressure: { + default: "mdi:thermometer-lines", + }, + battery: { + default: "mdi:battery", + }, + carbon_dioxide: { + default: "mdi:molecule-co2", + }, + carbon_monoxide: { + default: "mdi:molecule-co", + }, + current: { + default: "mdi:current-ac", + }, + data_rate: { + default: "mdi:transmission-tower", + }, + data_size: { + default: "mdi:database", + }, + distance: { + default: "mdi:arrow-left-right", + }, + duration: { + default: "mdi:progress-clock", + }, + energy: { + default: "mdi:lightning-bolt", + }, + energy_storage: { + default: "mdi:car-battery", + }, + frequency: { + default: "mdi:sine-wave", + }, + gas: { + default: "mdi:meter-gas", + }, + humidity: { + default: "mdi:water-percent", + }, + illuminance: { + default: "mdi:brightness-5", + }, + irradiance: { + default: "mdi:sun-wireless", + }, + moisture: { + default: "mdi:water-percent", + }, + monetary: { + default: "mdi:cash", + }, + nitrogen_dioxide: { + default: "mdi:molecule", + }, + nitrogen_monoxide: { + default: "mdi:molecule", + }, + nitrous_oxide: { + default: "mdi:molecule", + }, + ozone: { + default: "mdi:molecule", + }, + ph: { + default: "mdi:ph", + }, + pm1: { + default: "mdi:molecule", + }, + pm10: { + default: "mdi:molecule", + }, + pm25: { + default: "mdi:molecule", + }, + power: { + default: "mdi:flash", + }, + power_factor: { + default: "mdi:angle-acute", + }, + precipitation: { + default: "mdi:weather-rainy", + }, + precipitation_intensity: { + default: "mdi:weather-pouring", + }, + pressure: { + default: "mdi:gauge", + }, + reactive_power: { + default: "mdi:flash", + }, + signal_strength: { + default: "mdi:wifi", + }, + sound_pressure: { + default: "mdi:ear-hearing", + }, + speed: { + default: "mdi:speedometer", + }, + sulfur_dioxide: { + default: "mdi:molecule", + }, + temperature: { + default: "mdi:thermometer", + }, + volatile_organic_compounds: { + default: "mdi:molecule", + }, + volatile_organic_compounds_parts: { + default: "mdi:molecule", + }, + voltage: { + default: "mdi:sine-wave", + }, + volume: { + default: "mdi:car-coolant-level", + }, + volume_storage: { + default: "mdi:storage-tank", + }, + water: { + default: "mdi:water", + }, + weight: { + default: "mdi:weight", + }, + wind_speed: { + default: "mdi:weather-windy", + }, + }, + select: { + _: { + default: "mdi:format-list-bulleted", + }, + }, + tts: { + _: { + default: "mdi:speaker-message", + }, + }, + datetime: { + _: { + default: "mdi:calendar-clock", + }, + }, + vacuum: { + _: { + default: "mdi:robot-vacuum", + }, + }, + wake_word: { + _: { + default: "mdi:chat-sleep", + }, + }, + light: { + _: { + default: "mdi:lightbulb", + }, + }, + alarm_control_panel: { + _: { + default: "mdi:shield", + state: { + armed_away: "mdi:shield-lock", + armed_custom_bypass: "mdi:security", + armed_home: "mdi:shield-home", + armed_night: "mdi:shield-moon", + armed_vacation: "mdi:shield-airplane", + disarmed: "mdi:shield-off", + pending: "mdi:shield-outline", + triggered: "mdi:bell-ring", + }, + }, + }, + text: { + _: { + default: "mdi:form-textbox", + }, + }, + lawn_mower: { + _: { + default: "mdi:robot-mower", + }, + }, + siren: { + _: { + default: "mdi:bullhorn", + }, + }, + input_boolean: { + _: { + default: "mdi:check-circle-outline", + state: { + off: "mdi:close-circle-outline", + }, + }, + }, + lock: { + _: { + default: "mdi:lock", + state: { + jammed: "mdi:lock-alert", + locking: "mdi:lock-clock", + unlocked: "mdi:lock-open", + unlocking: "mdi:lock-clock", + }, + }, + }, + calendar: { + _: { + default: "mdi:calendar", + state: { + on: "mdi:calendar-check", + off: "mdi:calendar-blank", + }, + }, + }, + image: { + _: { + default: "mdi:image", + }, + }, + device_tracker: { + _: { + default: "mdi:account", + state: { + not_home: "mdi:account-arrow-right", + }, + }, + }, + scene: { + _: { + default: "mdi:palette", + }, + }, + script: { + _: { + default: "mdi:script-text", + state: { + on: "mdi:script-text-play", + }, + }, + }, + todo: { + _: { + default: "mdi:clipboard-list", + }, + }, + cover: { + _: { + default: "mdi:window-open", + state: { + closed: "mdi:window-closed", + closing: "mdi:arrow-down-box", + opening: "mdi:arrow-up-box", + }, + }, + blind: { + default: "mdi:blinds-horizontal", + state: { + closed: "mdi:blinds-horizontal-closed", + closing: "mdi:arrow-down-box", + opening: "mdi:arrow-up-box", + }, + }, + curtain: { + default: "mdi:curtains", + state: { + closed: "mdi:curtains-closed", + closing: "mdi:arrow-collapse-horizontal", + opening: "mdi:arrow-split-vertical", + }, + }, + damper: { + default: "mdi:circle", + state: { + closed: "mdi:circle-slice-8", + }, + }, + door: { + default: "mdi:door-open", + state: { + closed: "mdi:door-closed", + }, + }, + garage: { + default: "mdi:garage-open", + state: { + closed: "mdi:garage", + closing: "mdi:arrow-down-box", + opening: "mdi:arrow-up-box", + }, + }, + gate: { + default: "mdi:gate-open", + state: { + closed: "mdi:gate", + closing: "mdi:arrow-right", + opening: "mdi:arrow-right", + }, + }, + shade: { + default: "mdi:roller-shade", + state: { + closed: "mdi:roller-shade-closed", + closing: "mdi:arrow-down-box", + opening: "mdi:arrow-up-box", + }, + }, + shutter: { + default: "mdi:window-shutter-open", + state: { + closed: "mdi:window-shutter", + closing: "mdi:arrow-down-box", + opening: "mdi:arrow-up-box", + }, + }, + window: { + default: "mdi:window-open", + state: { + closed: "mdi:window-closed", + closing: "mdi:arrow-down-box", + opening: "mdi:arrow-up-box", + }, + }, + }, + switch: { + _: { + default: "mdi:toggle-switch-variant", + }, + switch: { + default: "mdi:toggle-switch-variant", + state: { + off: "mdi:toggle-switch-variant-off", + }, + }, + outlet: { + default: "mdi:power-plug", + state: { + off: "mdi:power-plug-off", + }, + }, + }, + button: { + _: { + default: "mdi:button-pointer", + }, + restart: { + default: "mdi:restart", + }, + identify: { + default: "mdi:crosshairs-question", + }, + update: { + default: "mdi:package-up", + }, + }, + water_heater: { + _: { + default: "mdi:water-boiler", + state: { + off: "mdi:water-boiler-off", + }, + state_attributes: { + operation_mode: { + default: "mdi:circle-medium", + state: { + eco: "mdi:leaf", + electric: "mdi:lightning-bolt", + gas: "mdi:fire-circle", + heat_pump: "mdi:heat-wave", + high_demand: "mdi:finance", + off: "mdi:power", + performance: "mdi:rocket-launch", + }, + }, + }, + }, + }, + binary_sensor: { + _: { + default: "mdi:radiobox-blank", + state: { + on: "mdi:checkbox-marked-circle", + }, + }, + battery: { + default: "mdi:battery", + state: { + on: "mdi:battery-outline", + }, + }, + battery_charging: { + default: "mdi:battery", + state: { + on: "mdi:battery-charging", + }, + }, + carbon_monoxide: { + default: "mdi:smoke-detector", + state: { + on: "mdi:smoke-detector-alert", + }, + }, + cold: { + default: "mdi:thermometer", + state: { + on: "mdi:snowflake", + }, + }, + connectivity: { + default: "mdi:close-network-outline", + state: { + on: "mdi:check-network-outline", + }, + }, + door: { + default: "mdi:door-closed", + state: { + on: "mdi:door-open", + }, + }, + garage_door: { + default: "mdi:garage", + state: { + on: "mdi:garage-open", + }, + }, + gas: { + default: "mdi:check-circle", + state: { + on: "mdi:alert-circle", + }, + }, + heat: { + default: "mdi:thermometer", + state: { + on: "mdi:fire", + }, + }, + light: { + default: "mdi:brightness-5", + state: { + on: "mdi:brightness-7", + }, + }, + lock: { + default: "mdi:lock", + state: { + on: "mdi:lock-open", + }, + }, + moisture: { + default: "mdi:water-off", + state: { + on: "mdi:water", + }, + }, + motion: { + default: "mdi:motion-sensor-off", + state: { + on: "mdi:motion-sensor", + }, + }, + moving: { + default: "mdi:arrow-right", + state: { + on: "mdi:octagon", + }, + }, + occupancy: { + default: "mdi:home-outline", + state: { + on: "mdi:home", + }, + }, + opening: { + default: "mdi:square", + state: { + on: "mdi:square-outline", + }, + }, + plug: { + default: "mdi:power-plug-off", + state: { + on: "mdi:power-plug", + }, + }, + power: { + default: "mdi:power-plug-off", + state: { + on: "mdi:power-plug", + }, + }, + presence: { + default: "mdi:home-outline", + state: { + on: "mdi:home", + }, + }, + problem: { + default: "mdi:check-circle", + state: { + on: "mdi:alert-circle", + }, + }, + running: { + default: "mdi:stop", + state: { + on: "mdi:play", + }, + }, + safety: { + default: "mdi:check-circle", + state: { + on: "mdi:alert-circle", + }, + }, + smoke: { + default: "mdi:smoke-detector-variant", + state: { + on: "mdi:smoke-detector-variant-alert", + }, + }, + sound: { + default: "mdi:music-note-off", + state: { + on: "mdi:music-note", + }, + }, + tamper: { + default: "mdi:check-circle", + state: { + on: "mdi:alert-circle", + }, + }, + update: { + default: "mdi:package", + state: { + on: "mdi:package-up", + }, + }, + vibration: { + default: "mdi:crop-portrait", + state: { + on: "mdi:vibrate", + }, + }, + window: { + default: "mdi:window-closed", + state: { + on: "mdi:window-open", + }, + }, + }, + sensor: { + _: { + default: "mdi:eye", + }, + apparent_power: { + default: "mdi:flash", + }, + aqi: { + default: "mdi:air-filter", + }, + atmospheric_pressure: { + default: "mdi:thermometer-lines", + }, + carbon_dioxide: { + default: "mdi:molecule-co2", + }, + carbon_monoxide: { + default: "mdi:molecule-co", + }, + current: { + default: "mdi:current-ac", + }, + data_rate: { + default: "mdi:transmission-tower", + }, + data_size: { + default: "mdi:database", + }, + date: { + default: "mdi:calendar", + }, + distance: { + default: "mdi:arrow-left-right", + }, + duration: { + default: "mdi:progress-clock", + }, + energy: { + default: "mdi:lightning-bolt", + }, + energy_storage: { + default: "mdi:car-battery", + }, + enum: { + default: "mdi:eye", + }, + frequency: { + default: "mdi:sine-wave", + }, + gas: { + default: "mdi:meter-gas", + }, + humidity: { + default: "mdi:water-percent", + }, + illuminance: { + default: "mdi:brightness-5", + }, + irradiance: { + default: "mdi:sun-wireless", + }, + moisture: { + default: "mdi:water-percent", + }, + monetary: { + default: "mdi:cash", + }, + nitrogen_dioxide: { + default: "mdi:molecule", + }, + nitrogen_monoxide: { + default: "mdi:molecule", + }, + nitrous_oxide: { + default: "mdi:molecule", + }, + ozone: { + default: "mdi:molecule", + }, + ph: { + default: "mdi:ph", + }, + pm1: { + default: "mdi:molecule", + }, + pm10: { + default: "mdi:molecule", + }, + pm25: { + default: "mdi:molecule", + }, + power: { + default: "mdi:flash", + }, + power_factor: { + default: "mdi:angle-acute", + }, + precipitation: { + default: "mdi:weather-rainy", + }, + precipitation_intensity: { + default: "mdi:weather-pouring", + }, + pressure: { + default: "mdi:gauge", + }, + reactive_power: { + default: "mdi:flash", + }, + signal_strength: { + default: "mdi:wifi", + }, + sound_pressure: { + default: "mdi:ear-hearing", + }, + speed: { + default: "mdi:speedometer", + }, + sulfur_dioxide: { + default: "mdi:molecule", + }, + temperature: { + default: "mdi:thermometer", + }, + timestamp: { + default: "mdi:clock", + }, + volatile_organic_compounds: { + default: "mdi:molecule", + }, + volatile_organic_compounds_parts: { + default: "mdi:molecule", + }, + voltage: { + default: "mdi:sine-wave", + }, + volume: { + default: "mdi:car-coolant-level", + }, + volume_storage: { + default: "mdi:storage-tank", + }, + water: { + default: "mdi:water", + }, + weight: { + default: "mdi:weight", + }, + wind_speed: { + default: "mdi:weather-windy", + }, + }, + humidifier: { + _: { + default: "mdi:air-humidifier", + state: { + off: "mdi:air-humidifier-off", + }, + state_attributes: { + action: { + default: "mdi:circle-medium", + state: { + drying: "mdi:arrow-down-bold", + humidifying: "mdi:arrow-up-bold", + idle: "mdi:clock-outline", + off: "mdi:power", + }, + }, + mode: { + default: "mdi:circle-medium", + state: { + auto: "mdi:refresh-auto", + away: "mdi:account-arrow-right", + baby: "mdi:baby-carriage", + boost: "mdi:rocket-launch", + comfort: "mdi:sofa", + eco: "mdi:leaf", + home: "mdi:home", + normal: "mdi:water-percent", + sleep: "mdi:power-sleep", + }, + }, + }, + }, + }, + valve: { + _: { + default: "mdi:pipe-valve", + }, + gas: { + default: "mdi:meter-gas", + }, + water: { + default: "mdi:pipe-valve", + }, + }, + time: { + _: { + default: "mdi:clock", + }, + }, + media_player: { + _: { + default: "mdi:cast", + state: { + off: "mdi:cast-off", + paused: "mdi:cast-connected", + playing: "mdi:cast-connected", + }, + }, + receiver: { + default: "mdi:audio-video", + state: { + off: "mdi:audio-video-off", + }, + }, + speaker: { + default: "mdi:speaker", + state: { + off: "mdi:speaker-off", + paused: "mdi:speaker-pause", + playing: "mdi:speaker-play", + }, + }, + tv: { + default: "mdi:television", + state: { + off: "mdi:television-off", + paused: "mdi:television-pause", + playing: "mdi:television-play", + }, + }, + }, + air_quality: { + _: { + default: "mdi:air-filter", + }, + }, + camera: { + _: { + default: "mdi:video", + state: { + off: "mdi:video-off", + }, + }, + }, + date: { + _: { + default: "mdi:calendar", + }, + }, + fan: { + _: { + default: "mdi:fan", + state: { + off: "mdi:fan-off", + }, + state_attributes: { + direction: { + default: "mdi:rotate-right", + state: { + reverse: "mdi:rotate-left", + }, + }, + }, + }, + }, + automation: { + _: { + default: "mdi:robot", + state: { + off: "mdi:robot-off", + unavailable: "mdi:robot-confused", + }, + }, + }, + weather: { + _: { + default: "mdi:weather-partly-cloudy", + state: { + "clear-night": "mdi:weather-night", + cloudy: "mdi:weather-cloudy", + exceptional: "mdi:alert-circle-outline", + fog: "mdi:weather-fog", + hail: "mdi:weather-hail", + lightning: "mdi:weather-lightning", + "lightning-rainy": "mdi:weather-lightning-rainy", + pouring: "mdi:weather-pouring", + rainy: "mdi:weather-rainy", + snowy: "mdi:weather-snowy", + "snowy-rainy": "mdi:weather-snowy-rainy", + sunny: "mdi:weather-sunny", + windy: "mdi:weather-windy", + "windy-variant": "mdi:weather-windy-variant", + }, + }, + }, + climate: { + _: { + default: "mdi:thermostat", + state_attributes: { + fan_mode: { + default: "mdi:circle-medium", + state: { + diffuse: "mdi:weather-windy", + focus: "mdi:target", + high: "mdi:speedometer", + low: "mdi:speedometer-slow", + medium: "mdi:speedometer-medium", + middle: "mdi:speedometer-medium", + off: "mdi:fan-off", + on: "mdi:fan", + }, + }, + hvac_action: { + default: "mdi:circle-medium", + state: { + cooling: "mdi:snowflake", + drying: "mdi:water-percent", + fan: "mdi:fan", + heating: "mdi:fire", + idle: "mdi:clock-outline", + off: "mdi:power", + preheating: "mdi:heat-wave", + }, + }, + preset_mode: { + default: "mdi:circle-medium", + state: { + activity: "mdi:motion-sensor", + away: "mdi:account-arrow-right", + boost: "mdi:rocket-launch", + comfort: "mdi:sofa", + eco: "mdi:leaf", + home: "mdi:home", + sleep: "mdi:bed", + }, + }, + swing_mode: { + default: "mdi:circle-medium", + state: { + both: "mdi:arrow-all", + horizontal: "mdi:arrow-left-right", + off: "mdi:arrow-oscillating-off", + on: "mdi:arrow-oscillating", + vertical: "mdi:arrow-up-down", + }, + }, + }, + }, + }, + stt: { + _: { + default: "mdi:microphone-message", + }, + }, + update: { + _: { + default: "mdi:package-up", + state: { + off: "mdi:package", + }, + }, + }, + event: { + _: { + default: "mdi:eye-check", + }, + button: { + default: "mdi:gesture-tap-button", + }, + doorbell: { + default: "mdi:doorbell", + }, + motion: { + default: "mdi:motion-sensor", + }, + }, +}; From 8624853ec47308cdd4b5b839f76dc2b96e678699 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 8 Feb 2024 17:40:43 +0100 Subject: [PATCH 012/116] Fix cast launch screen (#19738) --- cast/src/receiver/layout/hc-launch-screen.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cast/src/receiver/layout/hc-launch-screen.ts b/cast/src/receiver/layout/hc-launch-screen.ts index 2b212860e8..49be9d0700 100644 --- a/cast/src/receiver/layout/hc-launch-screen.ts +++ b/cast/src/receiver/layout/hc-launch-screen.ts @@ -28,7 +28,6 @@ class HcLaunchScreen extends LitElement { :host { display: block; height: 100vh; - padding-top: 64px; background-color: white; font-size: 24px; } @@ -36,12 +35,13 @@ class HcLaunchScreen extends LitElement { display: flex; flex-direction: column; text-align: center; + align-items: center; + height: 100%; + justify-content: space-evenly; } img { - width: 717px; - height: 376px; - display: block; - margin: 0 auto; + max-width: 80%; + object-fit: cover; } .status { padding-right: 54px; From 045ff7a45e57e9030f69640d2be92d75e27695af Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 8 Feb 2024 17:43:50 +0100 Subject: [PATCH 013/116] cast allow empty view, pick first (#19739) --- cast/src/receiver/layout/hc-lovelace.ts | 5 ++++- cast/src/receiver/layout/hc-main.ts | 14 +++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cast/src/receiver/layout/hc-lovelace.ts b/cast/src/receiver/layout/hc-lovelace.ts index 0de33aa69d..01b6f03fb0 100644 --- a/cast/src/receiver/layout/hc-lovelace.ts +++ b/cast/src/receiver/layout/hc-lovelace.ts @@ -17,7 +17,7 @@ class HcLovelace extends LitElement { @property({ attribute: false }) public lovelaceConfig!: LovelaceConfig; - @property() public viewPath?: string | number; + @property() public viewPath?: string | number | null; @property() public urlPath: string | null = null; @@ -93,6 +93,9 @@ class HcLovelace extends LitElement { } private get _viewIndex() { + if (this.viewPath === null) { + return 0; + } const selectedView = this.viewPath; const selectedViewInt = parseInt(selectedView as string, 10); for (let i = 0; i < this.lovelaceConfig.views.length; i++) { diff --git a/cast/src/receiver/layout/hc-main.ts b/cast/src/receiver/layout/hc-main.ts index dcf7a80eec..64b821817a 100644 --- a/cast/src/receiver/layout/hc-main.ts +++ b/cast/src/receiver/layout/hc-main.ts @@ -51,10 +51,10 @@ export class HcMain extends HassElement { @state() private _lovelacePath: string | number | null = null; - @state() private _error?: string; - @state() private _urlPath?: string | null; + @state() private _error?: string; + private _hassUUID?: string; private _unsubLovelace?: UnsubscribeFunc; @@ -81,7 +81,7 @@ export class HcMain extends HassElement { if ( !this._lovelaceConfig || - this._lovelacePath === null || + this._urlPath === undefined || // Guard against part of HA not being loaded yet. !this.hass || !this.hass.states || @@ -99,8 +99,8 @@ export class HcMain extends HassElement { `; @@ -226,9 +226,9 @@ export class HcMain extends HassElement { this.initializeHass(auth, connection); if (this._hassUUID !== msg.hassUUID) { this._hassUUID = msg.hassUUID; - this._lovelacePath = null; - this._urlPath = undefined; this._lovelaceConfig = undefined; + this._urlPath = undefined; + this._lovelacePath = null; if (this._unsubLovelace) { this._unsubLovelace(); this._unsubLovelace = undefined; @@ -285,7 +285,7 @@ export class HcMain extends HassElement { ], }; this._urlPath = "energy"; - this._lovelacePath = 0; + this._lovelacePath = null; this._sendStatus(); return; } From 17bd7f94761d612739ceae7a275797888851ca57 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 8 Feb 2024 17:45:37 +0100 Subject: [PATCH 014/116] Bumped version to 20240208.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 058f7ffd6f..73457456aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20240207.0" +version = "20240208.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From fac82fa185a840aca646e61909dd5c66d13b39d9 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 8 Feb 2024 18:08:35 +0100 Subject: [PATCH 015/116] Revert "Bumped version to 20240208.0" This reverts commit 17bd7f94761d612739ceae7a275797888851ca57. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 73457456aa..058f7ffd6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20240208.0" +version = "20240207.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 83da89437fbf47cb85a3b47d0ae0a445bfe63974 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 20:19:53 +0100 Subject: [PATCH 016/116] Update dependency lint-staged to v15.2.2 (#19737) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1ce11b7d20..590aec5fcb 100644 --- a/package.json +++ b/package.json @@ -215,7 +215,7 @@ "husky": "9.0.10", "instant-mocha": "1.5.2", "jszip": "3.10.1", - "lint-staged": "15.2.1", + "lint-staged": "15.2.2", "lit-analyzer": "2.0.3", "lodash.template": "4.5.0", "magic-string": "0.30.6", diff --git a/yarn.lock b/yarn.lock index a42f4f1a04..6d541d742a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9605,7 +9605,7 @@ __metadata: jszip: "npm:3.10.1" leaflet: "npm:1.9.4" leaflet-draw: "npm:1.0.4" - lint-staged: "npm:15.2.1" + lint-staged: "npm:15.2.2" lit: "npm:2.8.0" lit-analyzer: "npm:2.0.3" lodash.template: "npm:4.5.0" @@ -11184,9 +11184,9 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:15.2.1": - version: 15.2.1 - resolution: "lint-staged@npm:15.2.1" +"lint-staged@npm:15.2.2": + version: 15.2.2 + resolution: "lint-staged@npm:15.2.2" dependencies: chalk: "npm:5.3.0" commander: "npm:11.1.0" @@ -11200,7 +11200,7 @@ __metadata: yaml: "npm:2.3.4" bin: lint-staged: bin/lint-staged.js - checksum: 10/ee2e858b9afea01378ff3a3ff5fbed000f856c553f0ac570d9be6390e27299e586ae047d1efa77fdf17f15aeef0c53f6c658eeb24432748b9203698d929d0c76 + checksum: 10/5855ae7abf3ffdc2d66e8ad20759915e76544e7c4bcdfef78c82b5c126502284320d9fb0ecde554a6d07747311ab751d0bccbe3468aa5d5a7661774317cd7437 languageName: node linkType: hard From 618fee98ce852cba30056643115ca737957916f9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:27:25 -0500 Subject: [PATCH 017/116] Update typescript-eslint monorepo to v6.21.0 (#19747) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 104 +++++++++++++++++++++++++-------------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 590aec5fcb..cef88c2b79 100644 --- a/package.json +++ b/package.json @@ -183,8 +183,8 @@ "@types/tar": "6.1.11", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", - "@typescript-eslint/eslint-plugin": "6.20.0", - "@typescript-eslint/parser": "6.20.0", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", "@web/dev-server": "0.1.38", "@web/dev-server-rollup": "0.4.1", "babel-loader": "9.1.3", diff --git a/yarn.lock b/yarn.lock index 6d541d742a..ea8b9de8ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4515,15 +4515,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.20.0" +"@typescript-eslint/eslint-plugin@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.20.0" - "@typescript-eslint/type-utils": "npm:6.20.0" - "@typescript-eslint/utils": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" + "@typescript-eslint/scope-manager": "npm:6.21.0" + "@typescript-eslint/type-utils": "npm:6.21.0" + "@typescript-eslint/utils": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -4536,44 +4536,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/dee6a2392c831e6ae69611ecc4de06e66a7b16f6bf6d8e3bfd25091eb14d88c9d0bb9c9cd634efcfa318902341f7a459cf48f713d55cb1d610145ca1f52af4d3 + checksum: 10/a57de0f630789330204cc1531f86cfc68b391cafb1ba67c8992133f1baa2a09d629df66e71260b040de4c9a3ff1252952037093c4128b0d56c4dbb37720b4c1d languageName: node linkType: hard -"@typescript-eslint/parser@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/parser@npm:6.20.0" +"@typescript-eslint/parser@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/parser@npm:6.21.0" dependencies: - "@typescript-eslint/scope-manager": "npm:6.20.0" - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/typescript-estree": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" + "@typescript-eslint/scope-manager": "npm:6.21.0" + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/typescript-estree": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/691062d47cae7977604ede848ffff3689162428a53577f298989f585954aa3a3450e7fd5c2b363d024cd5f16022c163cecf0f1f1d138234bbd78048050b4b8bf + checksum: 10/4d51cdbc170e72275efc5ef5fce48a81ec431e4edde8374f4d0213d8d370a06823e1a61ae31d502a5f1b0d1f48fc4d29a1b1b5c2dcf809d66d3872ccf6e46ac7 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/scope-manager@npm:6.20.0" +"@typescript-eslint/scope-manager@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/scope-manager@npm:6.21.0" dependencies: - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" - checksum: 10/2c1a644f2931454b34875f2e6dffad52a1fc7b6ac508d7d1ad3cd9da028a7dff9c6191feeea2c9ca691deba199ac9e83cbd0036914be4cd45b6954437f03c09a + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" + checksum: 10/fe91ac52ca8e09356a71dc1a2f2c326480f3cccfec6b2b6d9154c1a90651ab8ea270b07c67df5678956c3bbf0bbe7113ab68f68f21b20912ea528b1214197395 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/type-utils@npm:6.20.0" +"@typescript-eslint/type-utils@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/type-utils@npm:6.21.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.20.0" - "@typescript-eslint/utils": "npm:6.20.0" + "@typescript-eslint/typescript-estree": "npm:6.21.0" + "@typescript-eslint/utils": "npm:6.21.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -4581,23 +4581,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/bc2f2793cfec3463164b5f5ded31b4e169e21c3a1990c1ce4effe70a359c486d92fbbc4cd92758bbf1c30a468ad0839e0fa890bd452c707d0c294cb3a7b14021 + checksum: 10/d03fb3ee1caa71f3ce053505f1866268d7ed79ffb7fed18623f4a1253f5b8f2ffc92636d6fd08fcbaf5bd265a6de77bf192c53105131e4724643dfc910d705fc languageName: node linkType: hard -"@typescript-eslint/types@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/types@npm:6.20.0" - checksum: 10/74ed1761e27c3c1a29fd260fe51096f42cfb1472b20390d6df6ec41de0420208f379e809de416e81cd7c00fdc3d5550b2391872be56bf4a1b0c595f71db0b1ea +"@typescript-eslint/types@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/types@npm:6.21.0" + checksum: 10/e26da86d6f36ca5b6ef6322619f8ec55aabcd7d43c840c977ae13ae2c964c3091fc92eb33730d8be08927c9de38466c5323e78bfb270a9ff1d3611fe821046c5 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.20.0" +"@typescript-eslint/typescript-estree@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" dependencies: - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/visitor-keys": "npm:6.20.0" + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/visitor-keys": "npm:6.21.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -4607,34 +4607,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/55b280c6e71c79cb009ac80189a7f0e1aa9011bc7206c810bbb52d9703a894aa2817dfd44d947edf64d62f3aa0962e01f3423fcb21d2f39964a4840287d9e196 + checksum: 10/b32fa35fca2a229e0f5f06793e5359ff9269f63e9705e858df95d55ca2cd7fdb5b3e75b284095a992c48c5fc46a1431a1a4b6747ede2dd08929dc1cbacc589b8 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/utils@npm:6.20.0" +"@typescript-eslint/utils@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/utils@npm:6.21.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.20.0" - "@typescript-eslint/types": "npm:6.20.0" - "@typescript-eslint/typescript-estree": "npm:6.20.0" + "@typescript-eslint/scope-manager": "npm:6.21.0" + "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/typescript-estree": "npm:6.21.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 10/6d4604be6123e0073dd5e7dd357c95b370c678572d2e982478d0d6937d4d65f0cad0ac207b8b724f3bce239e64ba1ddd6bece11e1592734d8bf691177e6971e6 + checksum: 10/b404a2c55a425a79d054346ae123087d30c7ecf7ed7abcf680c47bf70c1de4fabadc63434f3f460b2fa63df76bc9e4a0b9fa2383bb8a9fcd62733fb5c4e4f3e3 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.20.0": - version: 6.20.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.20.0" +"@typescript-eslint/visitor-keys@npm:6.21.0": + version: 6.21.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" dependencies: - "@typescript-eslint/types": "npm:6.20.0" + "@typescript-eslint/types": "npm:6.21.0" eslint-visitor-keys: "npm:^3.4.1" - checksum: 10/df066c73f3880ad78880c442f307e58f026e6047d9caab9d7c356d13276f4fe466fab3e8d19cdb1e6749e87639cb7c4babcfe118f554fcd2d3929ce9f4983216 + checksum: 10/30422cdc1e2ffad203df40351a031254b272f9c6f2b7e02e9bfa39e3fc2c7b1c6130333b0057412968deda17a3a68a578a78929a8139c6acef44d9d841dc72e1 languageName: node linkType: hard @@ -9546,8 +9546,8 @@ __metadata: "@types/tar": "npm:6.1.11" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@typescript-eslint/eslint-plugin": "npm:6.20.0" - "@typescript-eslint/parser": "npm:6.20.0" + "@typescript-eslint/eslint-plugin": "npm:6.21.0" + "@typescript-eslint/parser": "npm:6.21.0" "@vaadin/combo-box": "npm:24.3.5" "@vaadin/vaadin-themable-mixin": "npm:24.3.5" "@vibrant/color": "npm:3.2.1-alpha.1" From 1bcb1e7768a20079273b88d4155bfd02d2c31dcb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:28:26 -0500 Subject: [PATCH 018/116] Update dependency magic-string to v0.30.7 (#19744) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index cef88c2b79..69413d8315 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,7 @@ "lint-staged": "15.2.2", "lit-analyzer": "2.0.3", "lodash.template": "4.5.0", - "magic-string": "0.30.6", + "magic-string": "0.30.7", "map-stream": "0.0.7", "mocha": "10.2.0", "object-hash": "3.0.0", diff --git a/yarn.lock b/yarn.lock index ea8b9de8ed..f1310933e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9610,7 +9610,7 @@ __metadata: lit-analyzer: "npm:2.0.3" lodash.template: "npm:4.5.0" luxon: "npm:3.4.4" - magic-string: "npm:0.30.6" + magic-string: "npm:0.30.7" map-stream: "npm:0.0.7" marked: "npm:11.2.0" memoize-one: "npm:6.0.0" @@ -11504,12 +11504,12 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:0.30.6, magic-string@npm:^0.30.3": - version: 0.30.6 - resolution: "magic-string@npm:0.30.6" +"magic-string@npm:0.30.7, magic-string@npm:^0.30.3": + version: 0.30.7 + resolution: "magic-string@npm:0.30.7" dependencies: "@jridgewell/sourcemap-codec": "npm:^1.4.15" - checksum: 10/046fbf11614a271e7214d32ca02a2af8d18b268403db9d50580f421ff2222261f1cd8bb02a938206130e3c43deefa6423ee4622c4f161212298570f22b4059bb + checksum: 10/883eaaf6792a3263e44f4bcdcd35ace272268e4b98ed5a770ad711947958d2f9fc683e474945e306e2bdc152b7e44d369ee312690d87025b9879fc63fbe1409c languageName: node linkType: hard From e21ad742b1d74945ab9414ffe50c938c0c79cd73 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:56:50 -0500 Subject: [PATCH 019/116] Update dependency @codemirror/search to v6.5.6 (#19762) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 69413d8315..3f8ed795e7 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@codemirror/commands": "6.3.3", "@codemirror/language": "6.10.1", "@codemirror/legacy-modes": "6.3.3", - "@codemirror/search": "6.5.5", + "@codemirror/search": "6.5.6", "@codemirror/state": "6.4.0", "@codemirror/view": "6.23.1", "@egjs/hammerjs": "2.0.17", diff --git a/yarn.lock b/yarn.lock index f1310933e2..96c8874c14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1508,14 +1508,14 @@ __metadata: languageName: node linkType: hard -"@codemirror/search@npm:6.5.5": - version: 6.5.5 - resolution: "@codemirror/search@npm:6.5.5" +"@codemirror/search@npm:6.5.6": + version: 6.5.6 + resolution: "@codemirror/search@npm:6.5.6" dependencies: "@codemirror/state": "npm:^6.0.0" "@codemirror/view": "npm:^6.0.0" crelt: "npm:^1.0.5" - checksum: 10/61707efa563edaea1d83f0680db63a953f3f1c125e50bd912d84d07129aa5ba1f3a775f62339f7931c01f5afd69db777c0571215bd0aaef51964c37ee932f6df + checksum: 10/6668a34b4617e909617d3d831627d74b7a7985e8cd86d396bfcb3e86262f2310fc029fd6c846f1b8f1e6768e75985c9f1b0b18b31e05341f06b5b75c1ffde38d languageName: node linkType: hard @@ -9461,7 +9461,7 @@ __metadata: "@codemirror/commands": "npm:6.3.3" "@codemirror/language": "npm:6.10.1" "@codemirror/legacy-modes": "npm:6.3.3" - "@codemirror/search": "npm:6.5.5" + "@codemirror/search": "npm:6.5.6" "@codemirror/state": "npm:6.4.0" "@codemirror/view": "npm:6.23.1" "@egjs/hammerjs": "npm:2.0.17" From 99d72ba817792ded524d3456db0465aa22cda2db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:58:45 -0500 Subject: [PATCH 020/116] Update dependency hls.js to v1.5.4 (#19757) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 3f8ed795e7..8856f5e8cd 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "element-internals-polyfill": "1.3.10", "fuse.js": "7.0.0", "google-timezones-json": "1.2.0", - "hls.js": "1.5.3", + "hls.js": "1.5.4", "home-assistant-js-websocket": "9.1.0", "idb-keyval": "6.2.1", "intl-messageformat": "10.5.11", diff --git a/yarn.lock b/yarn.lock index 96c8874c14..7394bb9294 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9437,10 +9437,10 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:1.5.3": - version: 1.5.3 - resolution: "hls.js@npm:1.5.3" - checksum: 10/c5b7cb6fddd6ad425a82e1b8b4f818552e6e242254b35c7d7c7b9d3beb4dd40cc85d8bbc32a5d1b57031305d7d9f4973f9d0f80b70351bb8597393f4629072fc +"hls.js@npm:1.5.4": + version: 1.5.4 + resolution: "hls.js@npm:1.5.4" + checksum: 10/b86ee818db7a9b3f576fa1fb6c325a91885d6018be6e6cf496a7e1e8fd9ae060979de4347fd6635af47e704170cb47a09ef0942a38409c165f9958044d232e4d languageName: node linkType: hard @@ -9594,7 +9594,7 @@ __metadata: gulp-merge-json: "npm:2.1.2" gulp-rename: "npm:2.0.0" gulp-zopfli-green: "npm:6.0.1" - hls.js: "npm:1.5.3" + hls.js: "npm:1.5.4" home-assistant-js-websocket: "npm:9.1.0" html-minifier-terser: "npm:7.2.0" husky: "npm:9.0.10" From 10650e8937f29e518b95d668ae2fcdcd8416c1bb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:19:33 -0500 Subject: [PATCH 021/116] Update dependency marked to v12 (#19706) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8856f5e8cd..56efe2f88d 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "leaflet-draw": "1.0.4", "lit": "2.8.0", "luxon": "3.4.4", - "marked": "11.2.0", + "marked": "12.0.0", "memoize-one": "6.0.0", "node-vibrant": "3.2.1-alpha.1", "proxy-polyfill": "0.3.2", diff --git a/yarn.lock b/yarn.lock index 7394bb9294..e2bf183586 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9612,7 +9612,7 @@ __metadata: luxon: "npm:3.4.4" magic-string: "npm:0.30.7" map-stream: "npm:0.0.7" - marked: "npm:11.2.0" + marked: "npm:12.0.0" memoize-one: "npm:6.0.0" mocha: "npm:10.2.0" node-vibrant: "npm:3.2.1-alpha.1" @@ -11582,12 +11582,12 @@ __metadata: languageName: node linkType: hard -"marked@npm:11.2.0": - version: 11.2.0 - resolution: "marked@npm:11.2.0" +"marked@npm:12.0.0": + version: 12.0.0 + resolution: "marked@npm:12.0.0" bin: marked: bin/marked.js - checksum: 10/0c8c0d263617a04f066db6f5adfed811a8eb78a685850d4d0b8b9ef351e416fb871813ea7ee7f94f4d5f67da98c3a2a4259b8684e4f93a6a673733fe9d9f2868 + checksum: 10/ac2e5a3ebf33f8636e65c1eb7f73267cbe101fea1ad08abab60d51e5b4fda30faa59050e2837dc03fb6dbf58f630485c8d01ae5b9d90d36bf4562d7f40c1d33e languageName: node linkType: hard From 71483e0bc7f33c510b2413b9222fed0f1b033def Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 22:23:51 -0500 Subject: [PATCH 022/116] Update dependency mocha to v10.3.0 (#19772) --- package.json | 2 +- yarn.lock | 48 ++++++++++++------------------------------------ 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 56efe2f88d..74d8a99827 100644 --- a/package.json +++ b/package.json @@ -220,7 +220,7 @@ "lodash.template": "4.5.0", "magic-string": "0.30.7", "map-stream": "0.0.7", - "mocha": "10.2.0", + "mocha": "10.3.0", "object-hash": "3.0.0", "open": "10.0.3", "pinst": "3.0.0", diff --git a/yarn.lock b/yarn.lock index e2bf183586..6d57a3e314 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9044,17 +9044,16 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0": - version: 7.2.0 - resolution: "glob@npm:7.2.0" +"glob@npm:8.1.0, glob@npm:^8.0.3": + version: 8.1.0 + resolution: "glob@npm:8.1.0" dependencies: fs.realpath: "npm:^1.0.0" inflight: "npm:^1.0.4" inherits: "npm:2" - minimatch: "npm:^3.0.4" + minimatch: "npm:^5.0.1" once: "npm:^1.3.0" - path-is-absolute: "npm:^1.0.0" - checksum: 10/bc78b6ea0735b6e23d20678aba4ae6a4760e8c9527e3c4683ac25b14e70f55f9531245dcf25959b70cbc4aa3dcce1fc37ab65fd026a4cbd70aa3a44880bd396b + checksum: 10/9aab1c75eb087c35dbc41d1f742e51d0507aa2b14c910d96fb8287107a10a22f4bbdce26fc0a3da4c69a20f7b26d62f1640b346a4f6e6becfff47f335bb1dc5e languageName: node linkType: hard @@ -9072,19 +9071,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.3": - version: 8.1.0 - resolution: "glob@npm:8.1.0" - dependencies: - fs.realpath: "npm:^1.0.0" - inflight: "npm:^1.0.4" - inherits: "npm:2" - minimatch: "npm:^5.0.1" - once: "npm:^1.3.0" - checksum: 10/9aab1c75eb087c35dbc41d1f742e51d0507aa2b14c910d96fb8287107a10a22f4bbdce26fc0a3da4c69a20f7b26d62f1640b346a4f6e6becfff47f335bb1dc5e - languageName: node - linkType: hard - "global-modules@npm:^1.0.0": version: 1.0.0 resolution: "global-modules@npm:1.0.0" @@ -9614,7 +9600,7 @@ __metadata: map-stream: "npm:0.0.7" marked: "npm:12.0.0" memoize-one: "npm:6.0.0" - mocha: "npm:10.2.0" + mocha: "npm:10.3.0" node-vibrant: "npm:3.2.1-alpha.1" object-hash: "npm:3.0.0" open: "npm:10.0.3" @@ -11763,7 +11749,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.1.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:3.1.2, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -11927,9 +11913,9 @@ __metadata: languageName: node linkType: hard -"mocha@npm:10.2.0": - version: 10.2.0 - resolution: "mocha@npm:10.2.0" +"mocha@npm:10.3.0": + version: 10.3.0 + resolution: "mocha@npm:10.3.0" dependencies: ansi-colors: "npm:4.1.1" browser-stdout: "npm:1.3.1" @@ -11938,13 +11924,12 @@ __metadata: diff: "npm:5.0.0" escape-string-regexp: "npm:4.0.0" find-up: "npm:5.0.0" - glob: "npm:7.2.0" + glob: "npm:8.1.0" he: "npm:1.2.0" js-yaml: "npm:4.1.0" log-symbols: "npm:4.1.0" minimatch: "npm:5.0.1" ms: "npm:2.1.3" - nanoid: "npm:3.3.3" serialize-javascript: "npm:6.0.0" strip-json-comments: "npm:3.1.1" supports-color: "npm:8.1.1" @@ -11955,7 +11940,7 @@ __metadata: bin: _mocha: bin/_mocha mocha: bin/mocha.js - checksum: 10/f7362898ae65e8fe716cfe62fd014b432d100c9611aaf5abe85ed14efcbfdd82f3bdf32c44bccf00c9059a264c7e8d93a69dd5b830652109052a92beffb7ea35 + checksum: 10/8b30b3fdb3f365fca7e069fce732e13db4192e1bf8f49e63ec67a3b7857c41cbcd162289ad22178db6ef405fb251b8a0dbb54e942d08c72abf45e1ec3fcdd052 languageName: node linkType: hard @@ -12015,15 +12000,6 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:3.3.3": - version: 3.3.3 - resolution: "nanoid@npm:3.3.3" - bin: - nanoid: bin/nanoid.cjs - checksum: 10/c703ed58a234b68245a8a4826dd25c1453a9017d34fa28bc58e7aa8247de87d854582fa2209d7aee04084cff9ce150be8fd30300abe567dc615d4e8e735f2d99 - languageName: node - linkType: hard - "nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" From eeaad86c4bd4259fa141ee604c1dc6d7d60ceabd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 22:25:11 -0500 Subject: [PATCH 023/116] Update vaadinWebComponents monorepo to v24.3.6 (#19771) --- package.json | 4 +- yarn.lock | 170 +++++++++++++++++++++++++-------------------------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/package.json b/package.json index 74d8a99827..c0ca8cf2dc 100644 --- a/package.json +++ b/package.json @@ -89,8 +89,8 @@ "@polymer/paper-toast": "3.0.1", "@polymer/polymer": "3.5.1", "@thomasloven/round-slider": "0.6.0", - "@vaadin/combo-box": "24.3.5", - "@vaadin/vaadin-themable-mixin": "24.3.5", + "@vaadin/combo-box": "24.3.6", + "@vaadin/vaadin-themable-mixin": "24.3.6", "@vibrant/color": "3.2.1-alpha.1", "@vibrant/core": "3.2.1-alpha.1", "@vibrant/quantizer-mmcq": "3.2.1-alpha.1", diff --git a/yarn.lock b/yarn.lock index 6d57a3e314..9ff7c0e335 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4645,128 +4645,128 @@ __metadata: languageName: node linkType: hard -"@vaadin/a11y-base@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/a11y-base@npm:24.3.5" +"@vaadin/a11y-base@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/a11y-base@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.3.5" + "@vaadin/component-base": "npm:~24.3.6" lit: "npm:^3.0.0" - checksum: 10/17120df44a410e9c0b449b51c0b3380e6a2a0f92b7ea37746c3b20070849df535fa81ca4513219863e15009a5b04427b294fd467c692330f37b89db333d550f2 + checksum: 10/ba101554e829c569fe71cd05642e00437fc3bad7a4a106421843410459bb874eb6f484ca8ebb17f215577aa2e517dd64a84509d07dcd96bd0ab8eb0b62f30346 languageName: node linkType: hard -"@vaadin/combo-box@npm:24.3.5": - version: 24.3.5 - resolution: "@vaadin/combo-box@npm:24.3.5" +"@vaadin/combo-box@npm:24.3.6": + version: 24.3.6 + resolution: "@vaadin/combo-box@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.3.5" - "@vaadin/component-base": "npm:~24.3.5" - "@vaadin/field-base": "npm:~24.3.5" - "@vaadin/input-container": "npm:~24.3.5" - "@vaadin/item": "npm:~24.3.5" - "@vaadin/lit-renderer": "npm:~24.3.5" - "@vaadin/overlay": "npm:~24.3.5" - "@vaadin/vaadin-lumo-styles": "npm:~24.3.5" - "@vaadin/vaadin-material-styles": "npm:~24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:~24.3.5" - checksum: 10/ca61dc04d997eb2c4ba0e6433f199b139ed8e310a29cad2962c6ed610bafd8103638876c1f01ebe97b284fca535e5f86f7d6c4d48931e5e99f488675df197660 + "@vaadin/a11y-base": "npm:~24.3.6" + "@vaadin/component-base": "npm:~24.3.6" + "@vaadin/field-base": "npm:~24.3.6" + "@vaadin/input-container": "npm:~24.3.6" + "@vaadin/item": "npm:~24.3.6" + "@vaadin/lit-renderer": "npm:~24.3.6" + "@vaadin/overlay": "npm:~24.3.6" + "@vaadin/vaadin-lumo-styles": "npm:~24.3.6" + "@vaadin/vaadin-material-styles": "npm:~24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:~24.3.6" + checksum: 10/f05cb153a666227163031726b353b81500265ed614c3705cd5e81e9a6ec0235a0e207109b17958e9c5164b2c4d79ca25583400d4286cb1114221a0046d7c3e05 languageName: node linkType: hard -"@vaadin/component-base@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/component-base@npm:24.3.5" +"@vaadin/component-base@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/component-base@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" "@vaadin/vaadin-development-mode-detector": "npm:^2.0.0" "@vaadin/vaadin-usage-statistics": "npm:^2.1.0" lit: "npm:^3.0.0" - checksum: 10/cc9ef67334b0f145952d68a5ccf24fcce6a86f6cd548d07356e540a8f84f08d4567182856a597e8ed3c2650ffc0acbde181fe72502a6eb9ecf024afc3a564dde + checksum: 10/2bf4571a35f1d07a8da1a77c59d280add5b25a67aaf4e54a731ee10424125a028b01c06c54aa861f4892a5ce69cc4b1c84a37a1058588d10cd05bb34514601b6 languageName: node linkType: hard -"@vaadin/field-base@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/field-base@npm:24.3.5" +"@vaadin/field-base@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/field-base@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.3.5" - "@vaadin/component-base": "npm:~24.3.5" + "@vaadin/a11y-base": "npm:~24.3.6" + "@vaadin/component-base": "npm:~24.3.6" lit: "npm:^3.0.0" - checksum: 10/8ae5844791d1e4385780b254c149f5f320998c36eb932f7d04f08a8fcbb30f41d009e59738823c0b3836ce9214b047a17124721ca3fc622f137ef42030a68c1b + checksum: 10/71f407f8ca1801d7453a49f8d130e66c5a9999c8ddbfd1569a6e5141c372670b71cb195587a2a562a3965d2ac28e5fc022dcef4a98c96568ae78ad759ddb9d7c languageName: node linkType: hard -"@vaadin/icon@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/icon@npm:24.3.5" +"@vaadin/icon@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/icon@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.3.5" - "@vaadin/vaadin-lumo-styles": "npm:~24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:~24.3.5" + "@vaadin/component-base": "npm:~24.3.6" + "@vaadin/vaadin-lumo-styles": "npm:~24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:~24.3.6" lit: "npm:^3.0.0" - checksum: 10/f48d647e73e513c4d730faab4f4cd543918574ffdd3bcb6d1586d67b13e5428618b44b034ce49eda68fe2b806ae827f7625fc93eb2d94046128e779783cefe02 + checksum: 10/cffe77e5b1f6feb44b1f839c462580d5162cc1782e1701c292323068584e2a7747794473e46c3aa22c8fe6f1410a98e3bbd5a220ae6805c6759fec511e14675f languageName: node linkType: hard -"@vaadin/input-container@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/input-container@npm:24.3.5" +"@vaadin/input-container@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/input-container@npm:24.3.6" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.3.5" - "@vaadin/vaadin-lumo-styles": "npm:~24.3.5" - "@vaadin/vaadin-material-styles": "npm:~24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:~24.3.5" + "@vaadin/component-base": "npm:~24.3.6" + "@vaadin/vaadin-lumo-styles": "npm:~24.3.6" + "@vaadin/vaadin-material-styles": "npm:~24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:~24.3.6" lit: "npm:^3.0.0" - checksum: 10/b942c6657809e2a65cf317e0526786eb7206abff57dfe9fb0611245a5b5334307a589a78fa358b2d6f02bfe4534691295871db326db34bf4014c50fec5ed1f7f + checksum: 10/6fd86c09ba47ea2937896826f1646155d53d3e9d363031f2a6c81774f6224e8cff1038f400ebf75b9de3c0084c65952226e5309b4b5649b69a3e66ef8ad84e65 languageName: node linkType: hard -"@vaadin/item@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/item@npm:24.3.5" +"@vaadin/item@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/item@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.3.5" - "@vaadin/component-base": "npm:~24.3.5" - "@vaadin/vaadin-lumo-styles": "npm:~24.3.5" - "@vaadin/vaadin-material-styles": "npm:~24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:~24.3.5" - checksum: 10/1a428afd6f26f3f35465469d891a14198fb0bf46287e624139c585845a17352d9d013a54f96343b1790f8a9de2cb9796f2014ec58bc794d1215329038505313a + "@vaadin/a11y-base": "npm:~24.3.6" + "@vaadin/component-base": "npm:~24.3.6" + "@vaadin/vaadin-lumo-styles": "npm:~24.3.6" + "@vaadin/vaadin-material-styles": "npm:~24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:~24.3.6" + checksum: 10/d38f8219c8e9ba6acb833444eca2894723e83c06ce7557d0796042969899323ca9c19331639f6c677b49be79cda705da7d0d4251cd8370003df39f448f668835 languageName: node linkType: hard -"@vaadin/lit-renderer@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/lit-renderer@npm:24.3.5" +"@vaadin/lit-renderer@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/lit-renderer@npm:24.3.6" dependencies: lit: "npm:^3.0.0" - checksum: 10/232437d961102cdf5dee1828f0bd1ec7e2791bbce648cc0ac8860fb4e9a8ccad0dd49daa3bf1cd2d30cd5f0ad9dea149b9963bd847fba0299a7ae6775bf5dfdf + checksum: 10/5e2c93f9ccbe037a9d20338954dd4cd01511c3f481b0091623892f31194483661781650cbee52c64ebb8c3be99e1500cac90e460b82d781d6bf4c436ca1f2039 languageName: node linkType: hard -"@vaadin/overlay@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/overlay@npm:24.3.5" +"@vaadin/overlay@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/overlay@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" "@polymer/polymer": "npm:^3.0.0" - "@vaadin/a11y-base": "npm:~24.3.5" - "@vaadin/component-base": "npm:~24.3.5" - "@vaadin/vaadin-lumo-styles": "npm:~24.3.5" - "@vaadin/vaadin-material-styles": "npm:~24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:~24.3.5" - checksum: 10/1e171da246fae2a8cde03ba020773c09bb67cf384fa683501d7e63ea97528dd025e65c7a4a6c7c98d9c743c06b496e6926f24d7b94aac251f68931b65ace2d91 + "@vaadin/a11y-base": "npm:~24.3.6" + "@vaadin/component-base": "npm:~24.3.6" + "@vaadin/vaadin-lumo-styles": "npm:~24.3.6" + "@vaadin/vaadin-material-styles": "npm:~24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:~24.3.6" + checksum: 10/f2f8a8125a957999a8b184ab56516b29856f0ba3f6ab75f7e0bcde8a6d477d0342f2fd1dbc2ee0ac7b8bc6b320cf9c22227503cf9854a7000792da6644ce2078 languageName: node linkType: hard @@ -4777,36 +4777,36 @@ __metadata: languageName: node linkType: hard -"@vaadin/vaadin-lumo-styles@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/vaadin-lumo-styles@npm:24.3.5" +"@vaadin/vaadin-lumo-styles@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/vaadin-lumo-styles@npm:24.3.6" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.3.5" - "@vaadin/icon": "npm:~24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:~24.3.5" - checksum: 10/d19c0bd8ad03aafee5cf24ef49c1f351972581b70af491e9a6b46011a2c761ec46c0897883f5dd9f6a555c85985d747a97455ebe9ee588ca8e4875a452fc6f1e + "@vaadin/component-base": "npm:~24.3.6" + "@vaadin/icon": "npm:~24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:~24.3.6" + checksum: 10/158bedb18919229dc743821bbac6510b50b825d901265b71512f980d478675f675491a129f65de955c74ca367a1d97d7fd5691283a255f507ed28522722a209e languageName: node linkType: hard -"@vaadin/vaadin-material-styles@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/vaadin-material-styles@npm:24.3.5" +"@vaadin/vaadin-material-styles@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/vaadin-material-styles@npm:24.3.6" dependencies: "@polymer/polymer": "npm:^3.0.0" - "@vaadin/component-base": "npm:~24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:~24.3.5" - checksum: 10/4685b035992389bf48745a7591380c8fa13dda32b6ff6df8685b16768e52fb35ab23e7e46805dd5eda419417660adf483ce600699fdd0476e9a74226ed3adc39 + "@vaadin/component-base": "npm:~24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:~24.3.6" + checksum: 10/76835be72e6d08727a38cdec11c54565c7319eb1010ee5404f78acc7b6d8e8f5c363f9c5e9c149507a42a7ee7b075e8e0aae78aeaa375f4db3b0a97ee0cabc15 languageName: node linkType: hard -"@vaadin/vaadin-themable-mixin@npm:24.3.5, @vaadin/vaadin-themable-mixin@npm:~24.3.5": - version: 24.3.5 - resolution: "@vaadin/vaadin-themable-mixin@npm:24.3.5" +"@vaadin/vaadin-themable-mixin@npm:24.3.6, @vaadin/vaadin-themable-mixin@npm:~24.3.6": + version: 24.3.6 + resolution: "@vaadin/vaadin-themable-mixin@npm:24.3.6" dependencies: "@open-wc/dedupe-mixin": "npm:^1.3.0" lit: "npm:^3.0.0" - checksum: 10/faab5b044dfae77a5f558d006ca7debc4f6134e7eef47df538c35fd611ba02be0ae59d66d9574c5ce1aeea60a005fc4213f11259760352f370eefa60c47024b6 + checksum: 10/93b2f78bf2ad8575caedb1a0f7742d6f7e3920c362d74da6addf2d00de152e9a6e588cfa0b47f1aea76322fd031ae65446d80c6015591c7e00e4124afe07ffbb languageName: node linkType: hard @@ -9534,8 +9534,8 @@ __metadata: "@types/webspeechapi": "npm:0.0.29" "@typescript-eslint/eslint-plugin": "npm:6.21.0" "@typescript-eslint/parser": "npm:6.21.0" - "@vaadin/combo-box": "npm:24.3.5" - "@vaadin/vaadin-themable-mixin": "npm:24.3.5" + "@vaadin/combo-box": "npm:24.3.6" + "@vaadin/vaadin-themable-mixin": "npm:24.3.6" "@vibrant/color": "npm:3.2.1-alpha.1" "@vibrant/core": "npm:3.2.1-alpha.1" "@vibrant/quantizer-mmcq": "npm:3.2.1-alpha.1" From 504921052418267c97935bf79181a43acf22492c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 07:51:26 +0100 Subject: [PATCH 024/116] Bump actions/upload-artifact from 4.3.0 to 4.3.1 (#19778) --- .github/workflows/ci.yaml | 4 ++-- .github/workflows/nightly.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2becc88dff..caf4282d52 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -89,7 +89,7 @@ jobs: env: IS_TEST: "true" - name: Upload bundle stats - uses: actions/upload-artifact@v4.3.0 + uses: actions/upload-artifact@v4.3.1 with: name: frontend-bundle-stats path: build/stats/*.json @@ -113,7 +113,7 @@ jobs: env: IS_TEST: "true" - name: Upload bundle stats - uses: actions/upload-artifact@v4.3.0 + uses: actions/upload-artifact@v4.3.1 with: name: supervisor-bundle-stats path: build/stats/*.json diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 1d62f1071f..41a0b6c272 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -57,14 +57,14 @@ jobs: run: tar -czvf translations.tar.gz translations - name: Upload build artifacts - uses: actions/upload-artifact@v4.3.0 + uses: actions/upload-artifact@v4.3.1 with: name: wheels path: dist/home_assistant_frontend*.whl if-no-files-found: error - name: Upload translations - uses: actions/upload-artifact@v4.3.0 + uses: actions/upload-artifact@v4.3.1 with: name: translations path: translations.tar.gz From eb3e756637d63ba4bc5ee92a40b873ec45f82761 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 08:23:29 +0100 Subject: [PATCH 025/116] Bump actions/setup-node from 4.0.1 to 4.0.2 (#19777) --- .github/workflows/cast_deployment.yaml | 4 ++-- .github/workflows/ci.yaml | 8 ++++---- .github/workflows/demo_deployment.yaml | 4 ++-- .github/workflows/design_deployment.yaml | 2 +- .github/workflows/design_preview.yaml | 2 +- .github/workflows/nightly.yaml | 2 +- .github/workflows/release.yaml | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cast_deployment.yaml b/.github/workflows/cast_deployment.yaml index 2aa9ce5168..c65362b228 100644 --- a/.github/workflows/cast_deployment.yaml +++ b/.github/workflows/cast_deployment.yaml @@ -26,7 +26,7 @@ jobs: ref: dev - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn @@ -62,7 +62,7 @@ jobs: ref: master - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index caf4282d52..cf36bba4e8 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,7 +26,7 @@ jobs: - name: Check out files from GitHub uses: actions/checkout@v4.1.1 - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn @@ -60,7 +60,7 @@ jobs: - name: Check out files from GitHub uses: actions/checkout@v4.1.1 - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn @@ -78,7 +78,7 @@ jobs: - name: Check out files from GitHub uses: actions/checkout@v4.1.1 - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn @@ -102,7 +102,7 @@ jobs: - name: Check out files from GitHub uses: actions/checkout@v4.1.1 - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn diff --git a/.github/workflows/demo_deployment.yaml b/.github/workflows/demo_deployment.yaml index 9aa8219845..8e2fa8ea3d 100644 --- a/.github/workflows/demo_deployment.yaml +++ b/.github/workflows/demo_deployment.yaml @@ -27,7 +27,7 @@ jobs: ref: dev - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn @@ -63,7 +63,7 @@ jobs: ref: master - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn diff --git a/.github/workflows/design_deployment.yaml b/.github/workflows/design_deployment.yaml index 7fd0110210..829dd9ee61 100644 --- a/.github/workflows/design_deployment.yaml +++ b/.github/workflows/design_deployment.yaml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4.1.1 - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn diff --git a/.github/workflows/design_preview.yaml b/.github/workflows/design_preview.yaml index b24148d66d..439e0c05b5 100644 --- a/.github/workflows/design_preview.yaml +++ b/.github/workflows/design_preview.yaml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v4.1.1 - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 41a0b6c272..d382bcfbed 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -28,7 +28,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index dc843252dd..013ad89f62 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -34,7 +34,7 @@ jobs: python-version: ${{ env.PYTHON_VERSION }} - name: Setup Node - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version-file: ".nvmrc" cache: yarn From cd167ac64594d52be57ce8bae9a01e2c60be7308 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:12:23 -0500 Subject: [PATCH 026/116] Update dependency @codemirror/view to v6.24.0 (#19781) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index c0ca8cf2dc..e680f9134f 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@codemirror/legacy-modes": "6.3.3", "@codemirror/search": "6.5.6", "@codemirror/state": "6.4.0", - "@codemirror/view": "6.23.1", + "@codemirror/view": "6.24.0", "@egjs/hammerjs": "2.0.17", "@formatjs/intl-datetimeformat": "6.12.2", "@formatjs/intl-displaynames": "6.6.6", diff --git a/yarn.lock b/yarn.lock index 9ff7c0e335..9de8fbab1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1526,14 +1526,14 @@ __metadata: languageName: node linkType: hard -"@codemirror/view@npm:6.23.1, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0": - version: 6.23.1 - resolution: "@codemirror/view@npm:6.23.1" +"@codemirror/view@npm:6.24.0, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0": + version: 6.24.0 + resolution: "@codemirror/view@npm:6.24.0" dependencies: "@codemirror/state": "npm:^6.4.0" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/42e6b73bcad6bf5d2e9578c54d166c63c4b1c7c7c7806b6f6b4bead8683dc7fcca52201a02a1f9b8ccf120a4ad87e7dcd68f09d9d3e416304dad41a75e20da82 + checksum: 10/f8d4d4403d9483801cd4bab19abe6eba662d1c21f2726a54dbf9779770b9c034017fd28cf3c6cbf1230d1ec00370d5725dc4c9e81c55b9a534bfcf9fae264f4a languageName: node linkType: hard @@ -9449,7 +9449,7 @@ __metadata: "@codemirror/legacy-modes": "npm:6.3.3" "@codemirror/search": "npm:6.5.6" "@codemirror/state": "npm:6.4.0" - "@codemirror/view": "npm:6.23.1" + "@codemirror/view": "npm:6.24.0" "@egjs/hammerjs": "npm:2.0.17" "@formatjs/intl-datetimeformat": "npm:6.12.2" "@formatjs/intl-displaynames": "npm:6.6.6" From c5a3670838ac26dbb716606b4d52fea366c728ba Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 22:53:41 +0000 Subject: [PATCH 027/116] Update dependency prettier to v3.2.5 (#19711) * Update dependency prettier to v3.2.5 * Reformat --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Steve Repsher --- .devcontainer/devcontainer.json | 18 +++++++++--------- package.json | 2 +- tsconfig.json | 12 ++++++------ yarn.lock | 10 +++++----- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ca5bb5e97d..d4af4fbeba 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,12 +2,12 @@ "name": "Home Assistant Frontend", "build": { "dockerfile": "Dockerfile", - "context": "..", + "context": ".." }, "appPort": "8124:8123", "postStartCommand": "script/bootstrap", "containerEnv": { - "WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}", + "WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}" }, "customizations": { "vscode": { @@ -16,7 +16,7 @@ "esbenp.prettier-vscode", "runem.lit-plugin", "github.vscode-pull-request-github", - "eamodio.gitlens", + "eamodio.gitlens" ], "settings": { "files.eol": "\n", @@ -27,17 +27,17 @@ "editor.renderWhitespace": "boundary", "editor.rulers": [80], "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "files.trimTrailingWhitespace": true, "terminal.integrated.shell.linux": "/usr/bin/zsh", "gitlens.showWelcomeOnInstall": false, "gitlens.showWhatsNewAfterUpgrades": false, - "workbench.startupEditor": "none", - }, - }, - }, + "workbench.startupEditor": "none" + } + } + } } diff --git a/package.json b/package.json index e680f9134f..87ef1cc670 100644 --- a/package.json +++ b/package.json @@ -224,7 +224,7 @@ "object-hash": "3.0.0", "open": "10.0.3", "pinst": "3.0.0", - "prettier": "3.2.4", + "prettier": "3.2.5", "rollup": "2.79.1", "rollup-plugin-string": "3.0.0", "rollup-plugin-terser": "7.0.2", diff --git a/tsconfig.json b/tsconfig.json index 72918ea5ad..83c5f840ac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -42,13 +42,13 @@ // LitElement "no-property-visibility-mismatch": "error", // CSS - "no-invalid-css": "off", // warning does not work + "no-invalid-css": "off" // warning does not work }, "globalTags": ["google-cast-launcher"], "customHtmlData": [ - "./node_modules/@lrnwebcomponents/simple-tooltip/custom-elements.json", - ], - }, - ], - }, + "./node_modules/@lrnwebcomponents/simple-tooltip/custom-elements.json" + ] + } + ] + } } diff --git a/yarn.lock b/yarn.lock index 9de8fbab1e..df0718f035 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9605,7 +9605,7 @@ __metadata: object-hash: "npm:3.0.0" open: "npm:10.0.3" pinst: "npm:3.0.0" - prettier: "npm:3.2.4" + prettier: "npm:3.2.5" proxy-polyfill: "npm:0.3.2" punycode: "npm:2.3.1" qr-scanner: "npm:1.4.2" @@ -13042,12 +13042,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.2.4": - version: 3.2.4 - resolution: "prettier@npm:3.2.4" +"prettier@npm:3.2.5": + version: 3.2.5 + resolution: "prettier@npm:3.2.5" bin: prettier: bin/prettier.cjs - checksum: 10/e2b735d0552501b3a7ac8bd3ba3b6de2920bb35bd4cd02d08cb9057ebe3e96d83b9a7e4b903d987b7530a50223b12c74d107c154337236ae2c68156ba1e65cd2 + checksum: 10/d509f9da0b70e8cacc561a1911c0d99ec75117faed27b95cc8534cb2349667dee6351b0ca83fa9d5703f14127faa52b798de40f5705f02d843da133fc3aa416a languageName: node linkType: hard From b65dc47f725e5dadf9e5969c747d02bc2218013d Mon Sep 17 00:00:00 2001 From: Yosi Levy <37745463+yosilevy@users.noreply.github.com> Date: Tue, 13 Feb 2024 10:22:19 +0200 Subject: [PATCH 028/116] Drawer fix (#19789) --- src/components/ha-drawer.ts | 4 ++-- src/dialogs/notifications/notification-drawer.ts | 8 +++++++- src/layouts/home-assistant-main.ts | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/ha-drawer.ts b/src/components/ha-drawer.ts index 45a54623d5..8887d722af 100644 --- a/src/components/ha-drawer.ts +++ b/src/components/ha-drawer.ts @@ -3,7 +3,6 @@ import { styles } from "@material/mwc-drawer/mwc-drawer.css"; import { css, PropertyValues } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { mainWindow } from "../common/dom/get_main_window"; const blockingElements = (document as any).$blockingElements; @@ -34,7 +33,8 @@ export class HaDrawer extends DrawerBase { protected updated(changedProps: PropertyValues) { super.updated(changedProps); if (changedProps.has("direction")) { - if (mainWindow.document.dir === "rtl") { + this.mdcRoot.dir = this.direction; + if (this.direction === "rtl") { this._rtlStyle = document.createElement("style"); this._rtlStyle.innerHTML = ` .mdc-drawer--animate { diff --git a/src/dialogs/notifications/notification-drawer.ts b/src/dialogs/notifications/notification-drawer.ts index a1a15f8130..6a77edb730 100644 --- a/src/dialogs/notifications/notification-drawer.ts +++ b/src/dialogs/notifications/notification-drawer.ts @@ -14,6 +14,7 @@ import "./notification-item"; import "../../components/ha-header-bar"; import "../../components/ha-drawer"; import type { HaDrawer } from "../../components/ha-drawer"; +import { computeRTLDirection } from "../../common/util/compute_rtl"; @customElement("notification-drawer") export class HuiNotificationDrawer extends LitElement { @@ -92,7 +93,12 @@ export class HuiNotificationDrawer extends LitElement { }); return html` - +
${this.hass.localize("ui.notification_drawer.title")} diff --git a/src/layouts/home-assistant-main.ts b/src/layouts/home-assistant-main.ts index 29963ac45d..b984910c50 100644 --- a/src/layouts/home-assistant-main.ts +++ b/src/layouts/home-assistant-main.ts @@ -15,6 +15,7 @@ import "../components/ha-drawer"; import { showNotificationDrawer } from "../dialogs/notifications/show-notification-drawer"; import type { HomeAssistant, Route } from "../types"; import "./partial-panel-resolver"; +import { computeRTLDirection } from "../common/util/compute_rtl"; declare global { // for fire event @@ -61,6 +62,7 @@ export class HomeAssistantMain extends LitElement { Date: Tue, 13 Feb 2024 09:23:04 +0100 Subject: [PATCH 029/116] Fix My redirect for supervisor_logs (#19780) My link for supervisor_logs redirected to common logs after the removal of Supervisor panel. Leverage provider param added in #19355 to show Supervisor logs directly. --- src/panels/my/ha-panel-my.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/my/ha-panel-my.ts b/src/panels/my/ha-panel-my.ts index bac84192a2..100eeddf59 100644 --- a/src/panels/my/ha-panel-my.ts +++ b/src/panels/my/ha-panel-my.ts @@ -258,7 +258,7 @@ export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({ }, supervisor_logs: { // Moved from Supervisor panel in 2022.5 - redirect: "/config/logs", + redirect: "/config/logs?provider=supervisor", }, supervisor_info: { // Moved from Supervisor panel in 2022.5 From 208bfebc120929a65986031a100d5bab88759468 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 13 Feb 2024 03:24:02 -0500 Subject: [PATCH 030/116] Fix chart alignments (#19788) --- src/components/chart/ha-chart-base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index aa4a90fef8..1bcfa3871f 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -211,9 +211,9 @@ export class HaChartBase extends LitElement { height: `${ this.height ?? this._chartHeight ?? this.clientWidth / 2 }px`, - "padding-left": `${this._paddingYAxisInternal}`, + "padding-left": `${this._paddingYAxisInternal}px`, "padding-right": 0, - "padding-inline-start": `${this._paddingYAxisInternal}`, + "padding-inline-start": `${this._paddingYAxisInternal}px`, "padding-inline-end": 0, })} > From 553230ca239da63327bc4c1b4ba9c81a03b8ddfb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 14 Feb 2024 03:56:02 -0500 Subject: [PATCH 031/116] Change how external QR code scanning works (#19743) --- src/external_app/external_messaging.ts | 85 +++++++++++++++++--------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/src/external_app/external_messaging.ts b/src/external_app/external_messaging.ts index 8c346738b7..a994a669b9 100644 --- a/src/external_app/external_messaging.ts +++ b/src/external_app/external_messaging.ts @@ -35,13 +35,22 @@ interface EMOutgoingMessageConfigGet extends EMMessage { type: "config/get"; } -interface EMOutgoingMessageScanQRCode extends EMMessage { - type: "qr_code/scan"; +interface EMOutgoingMessageBarCodeScan extends EMMessage { + type: "bar_code/scan"; title: string; description: string; alternative_option_label?: string; } +interface EMOutgoingMessageBarCodeClose extends EMMessage { + type: "bar_code/close"; +} + +interface EMOutgoingMessageBarCodeNotify extends EMMessage { + type: "bar_code/notify"; + message: string; +} + interface EMOutgoingMessageMatterCommission extends EMMessage { type: "matter/commission"; } @@ -55,13 +64,6 @@ type EMOutgoingMessageWithAnswer = { request: EMOutgoingMessageConfigGet; response: ExternalConfig; }; - "qr_code/scan": { - request: EMOutgoingMessageScanQRCode; - response: - | EMIncomingMessageQRCodeResponseCanceled - | EMIncomingMessageQRCodeResponseAlternativeOptions - | EMIncomingMessageQRCodeResponseScanResult; - }; }; interface EMOutgoingMessageExoplayerPlayHLS extends EMMessage { @@ -124,20 +126,23 @@ interface EMOutgoingMessageAssistShow extends EMMessage { } type EMOutgoingMessageWithoutAnswer = - | EMOutgoingMessageHaptic - | EMOutgoingMessageConnectionStatus + | EMMessageResultError + | EMMessageResultSuccess | EMOutgoingMessageAppConfiguration - | EMOutgoingMessageTagWrite - | EMOutgoingMessageSidebarShow | EMOutgoingMessageAssistShow + | EMOutgoingMessageBarCodeClose + | EMOutgoingMessageBarCodeNotify + | EMOutgoingMessageBarCodeScan + | EMOutgoingMessageConnectionStatus | EMOutgoingMessageExoplayerPlayHLS | EMOutgoingMessageExoplayerResize | EMOutgoingMessageExoplayerStop - | EMOutgoingMessageThemeUpdate - | EMMessageResultSuccess - | EMMessageResultError + | EMOutgoingMessageHaptic + | EMOutgoingMessageImportThreadCredentials | EMOutgoingMessageMatterCommission - | EMOutgoingMessageImportThreadCredentials; + | EMOutgoingMessageSidebarShow + | EMOutgoingMessageTagWrite + | EMOutgoingMessageThemeUpdate; interface EMIncomingMessageRestart { id: number; @@ -172,17 +177,39 @@ interface EMIncomingMessageShowAutomationEditor { }; } -export interface EMIncomingMessageQRCodeResponseCanceled { - action: "canceled"; +export interface EMIncomingMessageBarCodeScanResult { + id: number; + type: "command"; + command: "bar_code/scan_result"; + payload: { + // A string decoded from the barcode data. + rawValue: string; + // https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API#supported_barcode_formats + format: + | "aztec" + | "code_128" + | "code_39" + | "code_93" + | "codabar" + | "data_matrix" + | "ean_13" + | "ean_8" + | "itf" + | "pdf417" + | "qr_code" + | "upc_a" + | "upc_e" + | "unknown"; + }; } -export interface EMIncomingMessageQRCodeResponseAlternativeOptions { - action: "alternative_options"; -} - -export interface EMIncomingMessageQRCodeResponseScanResult { - action: "scan_result"; - result: string; +export interface EMIncomingMessageBarCodeScanAborted { + id: number; + type: "command"; + command: "bar_code/aborted"; + payload: { + reason: "canceled" | "alternative_options"; + }; } export type EMIncomingMessageCommands = @@ -190,7 +217,9 @@ export type EMIncomingMessageCommands = | EMIncomingMessageShowNotifications | EMIncomingMessageToggleSidebar | EMIncomingMessageShowSidebar - | EMIncomingMessageShowAutomationEditor; + | EMIncomingMessageShowAutomationEditor + | EMIncomingMessageBarCodeScanResult + | EMIncomingMessageBarCodeScanAborted; type EMIncomingMessage = | EMMessageResultSuccess @@ -207,7 +236,7 @@ export interface ExternalConfig { canCommissionMatter: boolean; canImportThreadCredentials: boolean; hasAssist: boolean; - hasQRScanner: number; + hasBarCodeScanner: number; } export class ExternalMessaging { From fd06f28253a75399ac3af2cb6fb446aa6ebb99bc Mon Sep 17 00:00:00 2001 From: Yosi Levy <37745463+yosilevy@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:57:10 +0200 Subject: [PATCH 032/116] Patch simple tooltip for RTL (#19767) --- ...onents-simple-tooltip-npm-8.0.0-77591f2e0c.patch | 13 +++++++++++++ package.json | 2 +- yarn.lock | 11 ++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 .yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch diff --git a/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch b/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch new file mode 100644 index 0000000000..9b177dc614 --- /dev/null +++ b/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch @@ -0,0 +1,13 @@ +diff --git a/simple-tooltip.js b/simple-tooltip.js +index 78a87f6a223925f0e29fbedb268c85a142ec6985..3d686dd6a3d5a93342b4b01408089fc316b408ca 100644 +--- a/simple-tooltip.js ++++ b/simple-tooltip.js +@@ -195,6 +195,8 @@ class SimpleTooltip extends LitElement { + .hidden { + position: absolute; + left: -10000px; ++ inset-inline-start: -10000px; ++ inset-inline-end: initial; + top: auto; + width: 1px; + height: 1px; diff --git a/package.json b/package.json index 87ef1cc670..1b7775ceb0 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@lit-labs/motion": "1.0.7", "@lit-labs/observers": "2.0.2", "@lit-labs/virtualizer": "2.0.12", - "@lrnwebcomponents/simple-tooltip": "8.0.0", + "@lrnwebcomponents/simple-tooltip": "patch:@lrnwebcomponents/simple-tooltip@npm%3A8.0.0#~/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch", "@material/chips": "=14.0.0-canary.53b3cad2f.0", "@material/data-table": "=14.0.0-canary.53b3cad2f.0", "@material/mwc-base": "0.27.0", diff --git a/yarn.lock b/yarn.lock index df0718f035..da1c028eab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2167,6 +2167,15 @@ __metadata: languageName: node linkType: hard +"@lrnwebcomponents/simple-tooltip@patch:@lrnwebcomponents/simple-tooltip@npm%3A8.0.0#~/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch": + version: 8.0.0 + resolution: "@lrnwebcomponents/simple-tooltip@patch:@lrnwebcomponents/simple-tooltip@npm%3A8.0.0#~/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch::version=8.0.0&hash=91f01a" + dependencies: + lit: "npm:^3.1.0" + checksum: 10/25e2ec2e7379004784ede67419da99a57c2aeb5f7c5747069682b0ca4f28a4e4aaec03a6946d17b33b85407b625d9747af7d0e576f2f1172f8b57090f425fbf9 + languageName: node + linkType: hard + "@material/animation@npm:14.0.0-canary.53b3cad2f.0": version: 14.0.0-canary.53b3cad2f.0 resolution: "@material/animation@npm:14.0.0-canary.53b3cad2f.0" @@ -9472,7 +9481,7 @@ __metadata: "@lit-labs/observers": "npm:2.0.2" "@lit-labs/virtualizer": "npm:2.0.12" "@lokalise/node-api": "npm:12.1.0" - "@lrnwebcomponents/simple-tooltip": "npm:8.0.0" + "@lrnwebcomponents/simple-tooltip": "patch:@lrnwebcomponents/simple-tooltip@npm%3A8.0.0#~/.yarn/patches/@lrnwebcomponents-simple-tooltip-npm-8.0.0-77591f2e0c.patch" "@material/chips": "npm:=14.0.0-canary.53b3cad2f.0" "@material/data-table": "npm:=14.0.0-canary.53b3cad2f.0" "@material/mwc-base": "npm:0.27.0" From 4e22fea6e28413551710a81afcf66336b6b59d7d Mon Sep 17 00:00:00 2001 From: Jeremy Noesen Date: Wed, 14 Feb 2024 02:57:39 -0600 Subject: [PATCH 033/116] Fix status text centering for cast launch screen (#19748) --- cast/src/receiver/layout/hc-launch-screen.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cast/src/receiver/layout/hc-launch-screen.ts b/cast/src/receiver/layout/hc-launch-screen.ts index 49be9d0700..ba70fb7452 100644 --- a/cast/src/receiver/layout/hc-launch-screen.ts +++ b/cast/src/receiver/layout/hc-launch-screen.ts @@ -43,11 +43,6 @@ class HcLaunchScreen extends LitElement { max-width: 80%; object-fit: cover; } - .status { - padding-right: 54px; - padding-inline-end: 54px; - padding-inline-start: initial; - } `; } } From a3a099126e7a6bd5b7bc067dbf3676b820972d55 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:03:04 -0500 Subject: [PATCH 034/116] Fix to display correct weather attribute unit (#19750) --- src/common/entity/compute_attribute_display.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/common/entity/compute_attribute_display.ts b/src/common/entity/compute_attribute_display.ts index 77bc61bbd4..5e66ae757f 100644 --- a/src/common/entity/compute_attribute_display.ts +++ b/src/common/entity/compute_attribute_display.ts @@ -53,9 +53,7 @@ export const computeAttributeValueDisplay = ( if (domain === "weather") { unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute); - } - - if (TEMPERATURE_ATTRIBUTES.has(attribute)) { + } else if (TEMPERATURE_ATTRIBUTES.has(attribute)) { unit = config.unit_system.temperature; } From 33cdd51f00f48f79af60b412cd92fe0cbe072aee Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 14 Feb 2024 10:07:50 +0100 Subject: [PATCH 035/116] Improve lovelace card events (#19785) --- .../lovelace/components/hui-card-options.ts | 4 ++-- .../editor/card-editor/hui-dialog-create-card.ts | 4 ++-- .../editor/card-editor/hui-dialog-edit-card.ts | 16 +++++++--------- .../card-editor/show-create-card-dialog.ts | 2 +- .../editor/card-editor/show-edit-card-dialog.ts | 5 +++-- src/panels/lovelace/views/hui-view.ts | 10 ++++++++-- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index 7f59aaf3e9..8d960e15f8 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -275,9 +275,9 @@ export class HuiCardOptions extends LitElement { const cardConfig = this._currentView.cards![path[1]]; showEditCardDialog(this, { lovelaceConfig: this.lovelace!.config, - cardConfig, saveConfig: this.lovelace!.saveConfig, - path: [path[0]], + path: [path[0], null], + newCardConfig: cardConfig, }); } diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts index 4ce93735d4..3c7798411f 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -214,8 +214,8 @@ export class HuiCreateDialogCard showEditCardDialog(this, { lovelaceConfig: this._params!.lovelaceConfig, saveConfig: this._params!.saveConfig, - path: this._params!.path, - cardConfig: config, + path: [this._params!.path[0], null], + newCardConfig: config, }); this.closeDialog(); diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index 98427c14f4..44e4703567 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -87,12 +87,13 @@ export class HuiDialogEditCard const [view, card] = params.path; this._viewConfig = params.lovelaceConfig.views[view]; this._cardConfig = - card !== undefined ? this._viewConfig.cards![card] : params.cardConfig; + params.newCardConfig ?? + (card !== null ? this._viewConfig.cards![card] : undefined); this.large = false; if (this._cardConfig && !Object.isFrozen(this._cardConfig)) { this._cardConfig = deepFreeze(this._cardConfig); } - if (params.cardConfig) { + if (params.newCardConfig) { this._dirty = true; } } @@ -368,16 +369,13 @@ export class HuiDialogEditCard return; } this._saving = true; + const [view, card] = this._params!.path; await this._params!.saveConfig( - this._params!.path.length === 1 - ? addCard( - this._params!.lovelaceConfig, - this._params!.path as [number], - this._cardConfig! - ) + card === null + ? addCard(this._params!.lovelaceConfig, [view], this._cardConfig!) : replaceCard( this._params!.lovelaceConfig, - this._params!.path as [number, number], + [view, card], this._cardConfig! ) ); diff --git a/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts index dedd6d1b6a..230047d399 100644 --- a/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts @@ -4,7 +4,7 @@ import type { LovelaceConfig } from "../../../../data/lovelace/config/types"; export interface CreateCardDialogParams { lovelaceConfig: LovelaceConfig; saveConfig: (config: LovelaceConfig) => void; - path: [number] | [number, number]; + path: [number]; entities?: string[]; // We can pass entity id's that will be added to the config when a card is picked } diff --git a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts index f6b55e3b0e..46070fd7ff 100644 --- a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts @@ -5,8 +5,9 @@ import type { LovelaceConfig } from "../../../../data/lovelace/config/types"; export interface EditCardDialogParams { lovelaceConfig: LovelaceConfig; saveConfig: (config: LovelaceConfig) => void; - path: [number] | [number, number]; - cardConfig?: LovelaceCardConfig; + path: [number, number | null]; + // If specified, the card will be replaced with the new card. + newCardConfig?: LovelaceCardConfig; } export const importEditCardDialog = () => import("./hui-dialog-edit-card"); diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 1d78c5e7a8..4338bc9eff 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -31,13 +31,19 @@ import { LovelaceViewConfig, isStrategyView, } from "../../../data/lovelace/config/view"; +import { HASSDomEvent } from "../../../common/dom/fire_event"; declare global { // for fire event interface HASSDomEvents { "ll-create-card": undefined; - "ll-edit-card": { path: [number] | [number, number] }; - "ll-delete-card": { path: [number] | [number, number]; confirm: boolean }; + "ll-edit-card": { path: [number, number] }; + "ll-delete-card": { path: [number, number]; confirm: boolean }; + } + interface HTMLElementEventMap { + "ll-create-card": HASSDomEvent; + "ll-edit-card": HASSDomEvent; + "ll-delete-card": HASSDomEvent; } } From 226dad309c1ceb3a22a6716d1d0d56c88a3f3dcb Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Wed, 14 Feb 2024 04:08:14 -0500 Subject: [PATCH 036/116] Install libpcap for core in devcontainers (#19787) --- .devcontainer/devcontainer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d4af4fbeba..d6033abcd0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,6 +5,7 @@ "context": ".." }, "appPort": "8124:8123", + "postCreateCommand": "sudo apt update && sudo apt upgrade -y && sudo apt install -y libpcap-dev", "postStartCommand": "script/bootstrap", "containerEnv": { "WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}" From 2dc9d268eccaddeff1b14aea08144dca2496e0eb Mon Sep 17 00:00:00 2001 From: PolishKrowa <45701824+Mrredstone5230@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:16:24 -0500 Subject: [PATCH 037/116] Allowed ObjectSelector to work in forms (#19761) --- src/components/ha-form/compute-initial-ha-form-data.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ha-form/compute-initial-ha-form-data.ts b/src/components/ha-form/compute-initial-ha-form-data.ts index 92095b6204..93b1fc4d2d 100644 --- a/src/components/ha-form/compute-initial-ha-form-data.ts +++ b/src/components/ha-form/compute-initial-ha-form-data.ts @@ -54,7 +54,8 @@ export const computeInitialHaFormData = ( "icon" in selector || "template" in selector || "text" in selector || - "theme" in selector + "theme" in selector || + "object" in selector ) { data[field.name] = ""; } else if ("number" in selector) { From 8136cc8008d4e261cc39ca57e0a841b7f5d1983a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 14 Feb 2024 15:05:18 +0100 Subject: [PATCH 038/116] Cloud: allow remote enable (#19691) * Cloud: allow remote enable * core does this --- src/data/cloud.ts | 2 ++ .../config/cloud/account/cloud-remote-pref.ts | 27 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/data/cloud.ts b/src/data/cloud.ts index b498428f9a..c732b2a65f 100644 --- a/src/data/cloud.ts +++ b/src/data/cloud.ts @@ -18,6 +18,7 @@ export interface CloudPreferences { google_enabled: boolean; alexa_enabled: boolean; remote_enabled: boolean; + remote_allow_remote_enable: boolean; google_secure_devices_pin: string | undefined; cloudhooks: { [webhookId: string]: CloudWebhook }; alexa_report_state: boolean; @@ -139,6 +140,7 @@ export const updateCloudPref = ( google_report_state?: CloudPreferences["google_report_state"]; google_secure_devices_pin?: CloudPreferences["google_secure_devices_pin"]; tts_default_voice?: CloudPreferences["tts_default_voice"]; + remote_allow_remote_enable?: CloudPreferences["remote_allow_remote_enable"]; } ) => hass.callWS({ diff --git a/src/panels/config/cloud/account/cloud-remote-pref.ts b/src/panels/config/cloud/account/cloud-remote-pref.ts index bcfb4bdde7..b74e94dce3 100644 --- a/src/panels/config/cloud/account/cloud-remote-pref.ts +++ b/src/panels/config/cloud/account/cloud-remote-pref.ts @@ -13,6 +13,7 @@ import { CloudStatusLoggedIn, connectCloudRemote, disconnectCloudRemote, + updateCloudPref, } from "../../../../data/cloud"; import type { HomeAssistant } from "../../../../types"; import { showToast } from "../../../../util/toast"; @@ -29,7 +30,8 @@ export class CloudRemotePref extends LitElement { return nothing; } - const { remote_enabled } = this.cloudStatus.prefs; + const { remote_enabled, remote_allow_remote_enable } = + this.cloudStatus.prefs; const { remote_connected, @@ -126,6 +128,12 @@ export class CloudRemotePref extends LitElement { .path=${mdiContentCopy} @click=${this._copyURL} > + + +
@@ -160,6 +168,20 @@ export class CloudRemotePref extends LitElement { } } + private async _toggleAllowRemoteEnabledChanged(ev) { + const toggle = ev.target as HaSwitch; + + try { + await updateCloudPref(this.hass, { + remote_allow_remote_enable: toggle.checked, + }); + fireEvent(this, "ha-refresh-cloud-status"); + } catch (err: any) { + alert(err.message); + toggle.checked = !toggle.checked; + } + } + private async _copyURL(ev): Promise { const url = ev.currentTarget.url; await copyToClipboard(url); @@ -216,6 +238,9 @@ export class CloudRemotePref extends LitElement { color: var(--secondary-text-color); cursor: pointer; } + ha-formfield { + margin-top: 8px; + } `; } } From 84938ccc94e50b6528a222895da375daba3251d9 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 15 Feb 2024 04:04:40 -0500 Subject: [PATCH 039/116] stopPropagation on automation moveUp/moveDown (#19804) * stopPropagation on automation moveUp/moveDown * also conditions and triggers --- src/panels/config/automation/action/ha-automation-action.ts | 2 ++ .../config/automation/condition/ha-automation-condition.ts | 2 ++ src/panels/config/automation/trigger/ha-automation-trigger.ts | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/panels/config/automation/action/ha-automation-action.ts b/src/panels/config/automation/action/ha-automation-action.ts index 2cf2984cbe..2722574ac0 100644 --- a/src/panels/config/automation/action/ha-automation-action.ts +++ b/src/panels/config/automation/action/ha-automation-action.ts @@ -203,12 +203,14 @@ export default class HaAutomationAction extends LitElement { } private _moveUp(ev) { + ev.stopPropagation(); const index = (ev.target as any).index; const newIndex = index - 1; this._move(index, newIndex); } private _moveDown(ev) { + ev.stopPropagation(); const index = (ev.target as any).index; const newIndex = index + 1; this._move(index, newIndex); diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts index a615f9a1b4..d164a6ee21 100644 --- a/src/panels/config/automation/condition/ha-automation-condition.ts +++ b/src/panels/config/automation/condition/ha-automation-condition.ts @@ -227,12 +227,14 @@ export default class HaAutomationCondition extends LitElement { } private _moveUp(ev) { + ev.stopPropagation(); const index = (ev.target as any).index; const newIndex = index - 1; this._move(index, newIndex); } private _moveDown(ev) { + ev.stopPropagation(); const index = (ev.target as any).index; const newIndex = index + 1; this._move(index, newIndex); diff --git a/src/panels/config/automation/trigger/ha-automation-trigger.ts b/src/panels/config/automation/trigger/ha-automation-trigger.ts index 4da764f879..65935d8f5d 100644 --- a/src/panels/config/automation/trigger/ha-automation-trigger.ts +++ b/src/panels/config/automation/trigger/ha-automation-trigger.ts @@ -180,12 +180,14 @@ export default class HaAutomationTrigger extends LitElement { } private _moveUp(ev) { + ev.stopPropagation(); const index = (ev.target as any).index; const newIndex = index - 1; this._move(index, newIndex); } private _moveDown(ev) { + ev.stopPropagation(); const index = (ev.target as any).index; const newIndex = index + 1; this._move(index, newIndex); From a30a35f82fc2053948b883dc66eb2145a4a3859e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:26:46 -0500 Subject: [PATCH 040/116] Update dependency chai to v5.1.0 (#19809) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1b7775ceb0..4e7126ce8e 100644 --- a/package.json +++ b/package.json @@ -189,7 +189,7 @@ "@web/dev-server-rollup": "0.4.1", "babel-loader": "9.1.3", "babel-plugin-template-html-minifier": "4.1.0", - "chai": "5.0.3", + "chai": "5.1.0", "del": "7.1.0", "eslint": "8.56.0", "eslint-config-airbnb-base": "15.0.0", diff --git a/yarn.lock b/yarn.lock index da1c028eab..047de6e176 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6384,16 +6384,16 @@ __metadata: languageName: node linkType: hard -"chai@npm:5.0.3": - version: 5.0.3 - resolution: "chai@npm:5.0.3" +"chai@npm:5.1.0": + version: 5.1.0 + resolution: "chai@npm:5.1.0" dependencies: assertion-error: "npm:^2.0.1" check-error: "npm:^2.0.0" deep-eql: "npm:^5.0.1" loupe: "npm:^3.1.0" pathval: "npm:^2.0.0" - checksum: 10/d9b2bb0e4591b4a73ea98bb7a6423c4e50dd35f43ce4404be26126796af970d844269bb0b07b21866620c542f07957003b34c5cf60095e5a84878af6b120cc2d + checksum: 10/4d4eee5ec61eeff9e9dfe05ae49ef4b48f1cc8e14e4d4c72476482cea85d824aff0e76d5c4d357026fb276870d705341068eef98d240cd90f008b9bd250e7a69 languageName: node linkType: hard @@ -9556,7 +9556,7 @@ __metadata: app-datepicker: "npm:5.1.1" babel-loader: "npm:9.1.3" babel-plugin-template-html-minifier: "npm:4.1.0" - chai: "npm:5.0.3" + chai: "npm:5.1.0" chart.js: "npm:4.4.1" comlink: "npm:4.4.1" core-js: "npm:3.35.1" From 8548c9767b6a23e724544ba265498dc15aeae144 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 16 Feb 2024 04:47:24 -0500 Subject: [PATCH 041/116] Localize assist pipeline debug (#19812) localize assist pipeline debug --- .../debug/assist-pipeline-run-debug.ts | 54 ++++++++++++++----- src/translations/en.json | 17 +++++- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/panels/config/voice-assistants/debug/assist-pipeline-run-debug.ts b/src/panels/config/voice-assistants/debug/assist-pipeline-run-debug.ts index a2190dbc63..6137be81b1 100644 --- a/src/panels/config/voice-assistants/debug/assist-pipeline-run-debug.ts +++ b/src/panels/config/voice-assistants/debug/assist-pipeline-run-debug.ts @@ -50,7 +50,9 @@ export class AssistPipelineRunDebug extends LitElement { ${this._pipelineRuns.length > 0 ? html` @@ -59,13 +61,13 @@ export class AssistPipelineRunDebug extends LitElement { @click=${this._clearConversation} .disabled=${!this._finished} > - Clear + ${this.hass.localize("ui.common.clear")} - Download + ${this.hass.localize("ui.common.download")} ` : ""} @@ -81,7 +83,9 @@ export class AssistPipelineRunDebug extends LitElement { >
- Run Text Pipeline + ${this.hass.localize( + "ui.panel.config.voice_assistants.debug.pipeline.run_text_pipeline" + )} - Run Audio Pipeline + ${this.hass.localize( + "ui.panel.config.voice_assistants.debug.pipeline.run_audio_pipeline" + )} - Run Audio Pipeline with Wake Word detection + ${this.hass.localize( + "ui.panel.config.voice_assistants.debug.pipeline.run_audio_with_wake" + )}
` @@ -107,7 +115,9 @@ export class AssistPipelineRunDebug extends LitElement { ? html` @@ -115,7 +125,9 @@ export class AssistPipelineRunDebug extends LitElement { @click=${this._runTextPipeline} .disabled=${!this._finished} > - Send + ${this.hass.localize( + "ui.panel.config.voice_assistants.debug.pipeline.send" + )} ` : this._finished @@ -123,14 +135,22 @@ export class AssistPipelineRunDebug extends LitElement { "wake_word" ? html` - Continue listening for wake word + ${this.hass.localize( + "ui.panel.config.voice_assistants.debug.pipeline.continue_listening" + )} ` : html` - Continue talking + ${this.hass.localize( + "ui.panel.config.voice_assistants.debug.pipeline.continue_talking" + )} ` : html` - + Date: Fri, 16 Feb 2024 07:04:16 -0700 Subject: [PATCH 042/116] Add fan preset modes feature to Tile cards (#19618) * Add support for fan preset modes in Tile cards. Convert climate-preset-mode-card-feature to a more generic preset-modes-card-feature that supports fan entitys. * Split fan and climate preset modes features * Restore climate-preset-modes as an editable feature --- .../hui-fan-preset-modes-card-feature.ts | 231 ++++++++++++++++++ src/panels/lovelace/card-features/types.ts | 17 +- .../create-card-feature-element.ts | 6 +- .../hui-card-features-editor.ts | 20 +- ...ui-fan-preset-modes-card-feature-editor.ts | 133 ++++++++++ src/translations/en.json | 9 + 6 files changed, 401 insertions(+), 15 deletions(-) create mode 100644 src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts create mode 100644 src/panels/lovelace/editor/config-elements/hui-fan-preset-modes-card-feature-editor.ts diff --git a/src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts b/src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts new file mode 100644 index 0000000000..b4701dab84 --- /dev/null +++ b/src/panels/lovelace/card-features/hui-fan-preset-modes-card-feature.ts @@ -0,0 +1,231 @@ +import { mdiTuneVariant } from "@mdi/js"; +import { HassEntity } from "home-assistant-js-websocket"; +import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import { stopPropagation } from "../../../common/dom/stop_propagation"; +import { computeDomain } from "../../../common/entity/compute_domain"; +import { supportsFeature } from "../../../common/entity/supports-feature"; +import "../../../components/ha-attribute-icon"; +import "../../../components/ha-control-select"; +import type { ControlSelectOption } from "../../../components/ha-control-select"; +import "../../../components/ha-control-select-menu"; +import type { HaControlSelectMenu } from "../../../components/ha-control-select-menu"; +import { FanEntity, FanEntityFeature } from "../../../data/fan"; +import { UNAVAILABLE } from "../../../data/entity"; +import { HomeAssistant } from "../../../types"; +import { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types"; +import { FanPresetModesCardFeatureConfig } from "./types"; + +export const supportsFanPresetModesCardFeature = (stateObj: HassEntity) => { + const domain = computeDomain(stateObj.entity_id); + return ( + domain === "fan" && supportsFeature(stateObj, FanEntityFeature.PRESET_MODE) + ); +}; + +@customElement("hui-fan-preset-modes-card-feature") +class HuiFanPresetModesCardFeature + extends LitElement + implements LovelaceCardFeature +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public stateObj?: FanEntity; + + @state() private _config?: FanPresetModesCardFeatureConfig; + + @state() _currentPresetMode?: string; + + @query("ha-control-select-menu", true) + private _haSelect?: HaControlSelectMenu; + + static getStubConfig( + _, + stateObj?: HassEntity + ): FanPresetModesCardFeatureConfig { + return { + type: "fan-preset-modes", + style: "dropdown", + preset_modes: stateObj?.attributes.preset_modes || [], + }; + } + + public static async getConfigElement(): Promise { + await import( + "../editor/config-elements/hui-fan-preset-modes-card-feature-editor" + ); + return document.createElement("hui-fan-preset-modes-card-feature-editor"); + } + + public setConfig(config: FanPresetModesCardFeatureConfig): void { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + } + + protected willUpdate(changedProp: PropertyValues): void { + super.willUpdate(changedProp); + if (changedProp.has("stateObj") && this.stateObj) { + this._currentPresetMode = this.stateObj.attributes.preset_mode; + } + } + + protected updated(changedProps: PropertyValues) { + super.updated(changedProps); + if (this._haSelect && changedProps.has("hass")) { + const oldHass = changedProps.get("hass") as HomeAssistant | undefined; + if ( + this.hass && + this.hass.formatEntityAttributeValue !== + oldHass?.formatEntityAttributeValue + ) { + this._haSelect.layoutOptions(); + } + } + } + + private async _valueChanged(ev: CustomEvent) { + const presetMode = + (ev.detail as any).value ?? ((ev.target as any).value as string); + + const oldPresetMode = this.stateObj!.attributes.preset_mode; + + if (presetMode === oldPresetMode) return; + + this._currentPresetMode = presetMode; + + try { + await this._setMode(presetMode); + } catch (err) { + this._currentPresetMode = oldPresetMode; + } + } + + private async _setMode(mode: string) { + await this.hass!.callService("fan", "set_preset_mode", { + entity_id: this.stateObj!.entity_id, + preset_mode: mode, + }); + } + + protected render(): TemplateResult | null { + if ( + !this._config || + !this.hass || + !this.stateObj || + !supportsFanPresetModesCardFeature(this.stateObj) + ) { + return null; + } + + const stateObj = this.stateObj; + + const modes = stateObj.attributes.preset_modes || []; + + const options = modes + .filter((mode) => (this._config!.preset_modes || []).includes(mode)) + .map((mode) => ({ + value: mode, + label: this.hass!.formatEntityAttributeValue( + this.stateObj!, + "preset_mode", + mode + ), + icon: html``, + })); + + if (this._config.style === "icons") { + return html` +
+ + +
+ `; + } + + return html` +
+ + ${this._currentPresetMode + ? html`` + : html` + + `} + ${options.map( + (option) => html` + + ${option.icon}${option.label} + + ` + )} + +
+ `; + } + + static get styles() { + return css` + ha-control-select-menu { + box-sizing: border-box; + --control-select-menu-height: 40px; + --control-select-menu-border-radius: 10px; + line-height: 1.2; + display: block; + width: 100%; + } + ha-control-select { + --control-select-color: var(--feature-color); + --control-select-padding: 0; + --control-select-thickness: 40px; + --control-select-border-radius: 10px; + --control-select-button-border-radius: 10px; + } + .container { + padding: 0 12px 12px 12px; + width: auto; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-fan-preset-modes-card-feature": HuiFanPresetModesCardFeature; + } +} diff --git a/src/panels/lovelace/card-features/types.ts b/src/panels/lovelace/card-features/types.ts index e5c4d38858..f96427b4ba 100644 --- a/src/panels/lovelace/card-features/types.ts +++ b/src/panels/lovelace/card-features/types.ts @@ -26,6 +26,12 @@ export interface LightColorTempCardFeatureConfig { type: "light-color-temp"; } +export interface FanPresetModesCardFeatureConfig { + type: "fan-preset-modes"; + style?: "dropdown" | "icons"; + preset_modes?: string[]; +} + export interface FanSpeedCardFeatureConfig { type: "fan-speed"; } @@ -123,19 +129,20 @@ export type LovelaceCardFeatureConfig = | CoverPositionCardFeatureConfig | CoverTiltPositionCardFeatureConfig | CoverTiltCardFeatureConfig + | FanPresetModesCardFeatureConfig | FanSpeedCardFeatureConfig | HumidifierToggleCardFeatureConfig | HumidifierModesCardFeatureConfig | LawnMowerCommandsCardFeatureConfig | LightBrightnessCardFeatureConfig | LightColorTempCardFeatureConfig - | VacuumCommandsCardFeatureConfig + | NumericInputCardFeatureConfig + | SelectOptionsCardFeatureConfig | TargetHumidityCardFeatureConfig | TargetTemperatureCardFeatureConfig - | WaterHeaterOperationModesCardFeatureConfig - | SelectOptionsCardFeatureConfig - | NumericInputCardFeatureConfig - | UpdateActionsCardFeatureConfig; + | UpdateActionsCardFeatureConfig + | VacuumCommandsCardFeatureConfig + | WaterHeaterOperationModesCardFeatureConfig; export type LovelaceCardFeatureContext = { entity_id?: string; diff --git a/src/panels/lovelace/create-element/create-card-feature-element.ts b/src/panels/lovelace/create-element/create-card-feature-element.ts index 5508221430..8aec1360e3 100644 --- a/src/panels/lovelace/create-element/create-card-feature-element.ts +++ b/src/panels/lovelace/create-element/create-card-feature-element.ts @@ -6,6 +6,7 @@ import "../card-features/hui-cover-open-close-card-feature"; import "../card-features/hui-cover-position-card-feature"; import "../card-features/hui-cover-tilt-card-feature"; import "../card-features/hui-cover-tilt-position-card-feature"; +import "../card-features/hui-fan-preset-modes-card-feature"; import "../card-features/hui-fan-speed-card-feature"; import "../card-features/hui-humidifier-modes-card-feature"; import "../card-features/hui-humidifier-toggle-card-feature"; @@ -16,9 +17,9 @@ import "../card-features/hui-numeric-input-card-feature"; import "../card-features/hui-select-options-card-feature"; import "../card-features/hui-target-temperature-card-feature"; import "../card-features/hui-target-humidity-card-feature"; +import "../card-features/hui-update-actions-card-feature"; import "../card-features/hui-vacuum-commands-card-feature"; import "../card-features/hui-water-heater-operation-modes-card-feature"; -import "../card-features/hui-update-actions-card-feature"; import { LovelaceCardFeatureConfig } from "../card-features/types"; import { @@ -35,6 +36,7 @@ const TYPES: Set = new Set([ "cover-position", "cover-tilt-position", "cover-tilt", + "fan-preset-modes", "fan-speed", "humidifier-modes", "humidifier-toggle", @@ -45,9 +47,9 @@ const TYPES: Set = new Set([ "select-options", "target-humidity", "target-temperature", + "update-actions", "vacuum-commands", "water-heater-operation-modes", - "update-actions", ]); export const createCardFeatureElement = (config: LovelaceCardFeatureConfig) => diff --git a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts index d8fe23d0a7..ef1fcdfae0 100644 --- a/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts +++ b/src/panels/lovelace/editor/config-elements/hui-card-features-editor.ts @@ -27,6 +27,7 @@ import { supportsCoverOpenCloseCardFeature } from "../../card-features/hui-cover import { supportsCoverPositionCardFeature } from "../../card-features/hui-cover-position-card-feature"; import { supportsCoverTiltCardFeature } from "../../card-features/hui-cover-tilt-card-feature"; import { supportsCoverTiltPositionCardFeature } from "../../card-features/hui-cover-tilt-position-card-feature"; +import { supportsFanPresetModesCardFeature } from "../../card-features/hui-fan-preset-modes-card-feature"; import { supportsFanSpeedCardFeature } from "../../card-features/hui-fan-speed-card-feature"; import { supportsHumidifierModesCardFeature } from "../../card-features/hui-humidifier-modes-card-feature"; import { supportsHumidifierToggleCardFeature } from "../../card-features/hui-humidifier-toggle-card-feature"; @@ -55,34 +56,36 @@ const UI_FEATURE_TYPES = [ "cover-position", "cover-tilt-position", "cover-tilt", + "fan-preset-modes", "fan-speed", "humidifier-modes", "humidifier-toggle", "lawn-mower-commands", "light-brightness", "light-color-temp", + "numeric-input", "select-options", "target-humidity", "target-temperature", - "vacuum-commands", "update-actions", + "vacuum-commands", "water-heater-operation-modes", - "numeric-input", ] as const satisfies readonly FeatureType[]; type UiFeatureTypes = (typeof UI_FEATURE_TYPES)[number]; const EDITABLES_FEATURE_TYPES = new Set([ - "vacuum-commands", "alarm-modes", "climate-hvac-modes", - "humidifier-modes", - "water-heater-operation-modes", - "lawn-mower-commands", "climate-fan-modes", "climate-preset-modes", + "fan-preset-modes", + "humidifier-modes", + "lawn-mower-commands", "numeric-input", "update-actions", + "vacuum-commands", + "water-heater-operation-modes", ]); const SUPPORTS_FEATURE_TYPES: Record< @@ -97,6 +100,7 @@ const SUPPORTS_FEATURE_TYPES: Record< "cover-position": supportsCoverPositionCardFeature, "cover-tilt-position": supportsCoverTiltPositionCardFeature, "cover-tilt": supportsCoverTiltCardFeature, + "fan-preset-modes": supportsFanPresetModesCardFeature, "fan-speed": supportsFanSpeedCardFeature, "humidifier-modes": supportsHumidifierModesCardFeature, "humidifier-toggle": supportsHumidifierToggleCardFeature, @@ -104,12 +108,12 @@ const SUPPORTS_FEATURE_TYPES: Record< "light-brightness": supportsLightBrightnessCardFeature, "light-color-temp": supportsLightColorTempCardFeature, "numeric-input": supportsNumericInputCardFeature, + "select-options": supportsSelectOptionsCardFeature, "target-humidity": supportsTargetHumidityCardFeature, "target-temperature": supportsTargetTemperatureCardFeature, + "update-actions": supportsUpdateActionsCardFeature, "vacuum-commands": supportsVacuumCommandsCardFeature, "water-heater-operation-modes": supportsWaterHeaterOperationModesCardFeature, - "select-options": supportsSelectOptionsCardFeature, - "update-actions": supportsUpdateActionsCardFeature, }; const customCardFeatures = getCustomCardFeatures(); diff --git a/src/panels/lovelace/editor/config-elements/hui-fan-preset-modes-card-feature-editor.ts b/src/panels/lovelace/editor/config-elements/hui-fan-preset-modes-card-feature-editor.ts new file mode 100644 index 0000000000..35cfd7b8a6 --- /dev/null +++ b/src/panels/lovelace/editor/config-elements/hui-fan-preset-modes-card-feature-editor.ts @@ -0,0 +1,133 @@ +import { HassEntity } from "home-assistant-js-websocket"; +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { FormatEntityAttributeValueFunc } from "../../../../common/translations/entity-state"; +import { LocalizeFunc } from "../../../../common/translations/localize"; +import "../../../../components/ha-form/ha-form"; +import type { + HaFormSchema, + SchemaUnion, +} from "../../../../components/ha-form/types"; +import type { HomeAssistant } from "../../../../types"; +import { + FanPresetModesCardFeatureConfig, + LovelaceCardFeatureContext, +} from "../../card-features/types"; +import type { LovelaceCardFeatureEditor } from "../../types"; + +@customElement("hui-fan-preset-modes-card-feature-editor") +export class HuiFanPresetModesCardFeatureEditor + extends LitElement + implements LovelaceCardFeatureEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public context?: LovelaceCardFeatureContext; + + @state() private _config?: FanPresetModesCardFeatureConfig; + + public setConfig(config: FanPresetModesCardFeatureConfig): void { + this._config = config; + } + + private _schema = memoizeOne( + ( + localize: LocalizeFunc, + formatEntityAttributeValue: FormatEntityAttributeValueFunc, + stateObj?: HassEntity + ) => + [ + { + name: "style", + selector: { + select: { + multiple: false, + mode: "list", + options: ["dropdown", "icons"].map((mode) => ({ + value: mode, + label: localize( + `ui.panel.lovelace.editor.features.types.fan-preset-modes.style_list.${mode}` + ), + })), + }, + }, + }, + { + name: "preset_modes", + selector: { + select: { + multiple: true, + mode: "list", + options: + stateObj?.attributes.preset_modes?.map((mode) => ({ + value: mode, + label: formatEntityAttributeValue( + stateObj, + "preset_mode", + mode + ), + })) || [], + }, + }, + }, + ] as const satisfies readonly HaFormSchema[] + ); + + protected render() { + if (!this.hass || !this._config) { + return nothing; + } + + const stateObj = this.context?.entity_id + ? this.hass.states[this.context?.entity_id] + : undefined; + + const data: FanPresetModesCardFeatureConfig = { + style: "dropdown", + preset_modes: [], + ...this._config, + }; + + const schema = this._schema( + this.hass.localize, + this.hass.formatEntityAttributeValue, + stateObj + ); + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + fireEvent(this, "config-changed", { config: ev.detail.value }); + } + + private _computeLabelCallback = ( + schema: SchemaUnion> + ) => { + switch (schema.name) { + case "style": + case "preset_modes": + return this.hass!.localize( + `ui.panel.lovelace.editor.features.types.fan-preset-modes.${schema.name}` + ); + default: + return ""; + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-fan-preset-modes-card-feature-editor": HuiFanPresetModesCardFeatureEditor; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index aeb83fbbb4..ae81ca6a75 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5615,6 +5615,15 @@ }, "preset_modes": "Preset modes" }, + "fan-preset-modes": { + "label": "Fan preset modes", + "style": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style%]", + "style_list": { + "dropdown": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::dropdown%]", + "icons": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::style_list::icons%]" + }, + "preset_modes": "[%key:ui::panel::lovelace::editor::features::types::climate-preset-modes::preset_modes%]" + }, "humidifier-toggle": { "label": "Humidifier toggle" }, From 316756d06a6431e18d484733aae9ec750a0972cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:00:56 -0500 Subject: [PATCH 043/116] Update dependency eslint-plugin-unused-imports to v3.1.0 (#19814) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4e7126ce8e..ad4b0b90a6 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "eslint-plugin-import": "2.29.1", "eslint-plugin-lit": "1.11.0", "eslint-plugin-lit-a11y": "4.1.2", - "eslint-plugin-unused-imports": "3.0.0", + "eslint-plugin-unused-imports": "3.1.0", "eslint-plugin-wc": "2.0.4", "fancy-log": "2.0.0", "fs-extra": "11.2.0", diff --git a/yarn.lock b/yarn.lock index 047de6e176..8766ab76a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7952,18 +7952,18 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-unused-imports@npm:3.0.0": - version: 3.0.0 - resolution: "eslint-plugin-unused-imports@npm:3.0.0" +"eslint-plugin-unused-imports@npm:3.1.0": + version: 3.1.0 + resolution: "eslint-plugin-unused-imports@npm:3.1.0" dependencies: eslint-rule-composer: "npm:^0.3.0" peerDependencies: - "@typescript-eslint/eslint-plugin": ^6.0.0 - eslint: ^8.0.0 + "@typescript-eslint/eslint-plugin": 6 - 7 + eslint: 8 peerDependenciesMeta: "@typescript-eslint/eslint-plugin": optional: true - checksum: 10/9433b80d4efdf3f8e43a38a7662b279b310020f3a80ffd2bbc56a375804b367bedfbe5b611b1969963e2de3b392bf1f389e89d2af810594ea3ab913c7e219ba1 + checksum: 10/8ad49d6d343492f5a5e6398a57d2c66c28651410d83478b16eac8fadb466cf7785b43cd7cfe33e0fb9624c464c7a92b5b78b34ad2daf418e8ac8fed5c554e1ed languageName: node linkType: hard @@ -9576,7 +9576,7 @@ __metadata: eslint-plugin-import: "npm:2.29.1" eslint-plugin-lit: "npm:1.11.0" eslint-plugin-lit-a11y: "npm:4.1.2" - eslint-plugin-unused-imports: "npm:3.0.0" + eslint-plugin-unused-imports: "npm:3.1.0" eslint-plugin-wc: "npm:2.0.4" fancy-log: "npm:2.0.0" fs-extra: "npm:11.2.0" From 26600e3d78a16fe398f1bb51863451edf65588d7 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:02:42 -0500 Subject: [PATCH 044/116] Compact legend mode for statistics-graph-card (#19089) * Compact legend mode for statistics-graph-card * remove clearable from selector * Remove legend_mode, make compact default --- src/components/chart/ha-chart-base.ts | 89 ++++++++++++++++++------ src/components/chart/statistics-chart.ts | 50 ++++++++++++- 2 files changed, 117 insertions(+), 22 deletions(-) diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 1bcfa3871f..edccd2d22c 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -5,10 +5,18 @@ import type { ChartOptions, TooltipModel, } from "chart.js"; -import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit"; +import { + css, + CSSResultGroup, + html, + nothing, + LitElement, + PropertyValues, +} from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { styleMap } from "lit/directives/style-map"; +import { fireEvent } from "../../common/dom/fire_event"; import { clamp } from "../../common/number/clamp"; import { HomeAssistant } from "../../types"; import { debounce } from "../../common/util/debounce"; @@ -27,6 +35,11 @@ interface Tooltip left: string; } +export interface ChartDatasetExtra { + show_legend?: boolean; + legend_label?: string; +} + @customElement("ha-chart-base") export class HaChartBase extends LitElement { public chart?: Chart; @@ -38,6 +51,8 @@ export class HaChartBase extends LitElement { @property({ attribute: false }) public data: ChartData = { datasets: [] }; + @property({ attribute: false }) public extraData?: ChartDatasetExtra[]; + @property({ attribute: false }) public options?: ChartOptions; @property({ attribute: false }) public plugins?: any[]; @@ -46,6 +61,8 @@ export class HaChartBase extends LitElement { @property({ type: Number }) public paddingYAxis = 0; + @property({ type: Boolean }) public externalHidden = false; + @state() private _chartHeight?: number; @state() private _tooltip?: Tooltip; @@ -148,6 +165,19 @@ export class HaChartBase extends LitElement { } } + if (changedProps.has("data")) { + if (this.externalHidden) { + this._hiddenDatasets = new Set(); + if (this.data?.datasets) { + this.data.datasets.forEach((dataset, index) => { + if (dataset.hidden) { + this._hiddenDatasets.add(index); + } + }); + } + } + } + if (!this.hasUpdated || !this.chart) { return; } @@ -157,7 +187,7 @@ export class HaChartBase extends LitElement { return; } if (changedProps.has("data")) { - if (this._hiddenDatasets.size) { + if (this._hiddenDatasets.size && !this.externalHidden) { this.data.datasets.forEach((dataset, index) => { dataset.hidden = this._hiddenDatasets.has(index); }); @@ -175,25 +205,30 @@ export class HaChartBase extends LitElement { ${this.options?.plugins?.legend?.display === true ? html`
    - ${this.data.datasets.map( - (dataset, index) => - html`
  • -
    + this.extraData?.[index]?.show_legend === false + ? nothing + : html`
-
${dataset.label}
- ` + .title=${this.extraData?.[index]?.legend_label ?? + dataset.label} + > +
+
+ ${this.extraData?.[index]?.legend_label ?? + dataset.label} +
+ ` )}
` @@ -339,9 +374,19 @@ export class HaChartBase extends LitElement { if (this.chart.isDatasetVisible(index)) { this.chart.setDatasetVisibility(index, false); this._hiddenDatasets.add(index); + if (this.externalHidden) { + fireEvent(this, "dataset-hidden", { + index, + }); + } } else { this.chart.setDatasetVisibility(index, true); this._hiddenDatasets.delete(index); + if (this.externalHidden) { + fireEvent(this, "dataset-unhidden", { + index, + }); + } } this.chart.update("none"); this.requestUpdate("_hiddenDatasets"); @@ -486,4 +531,8 @@ declare global { interface HTMLElementTagNameMap { "ha-chart-base": HaChartBase; } + interface HASSDomEvents { + "dataset-hidden": { index: number }; + "dataset-unhidden": { index: number }; + } } diff --git a/src/components/chart/statistics-chart.ts b/src/components/chart/statistics-chart.ts index 0e2fa2a9f5..0c5597878c 100644 --- a/src/components/chart/statistics-chart.ts +++ b/src/components/chart/statistics-chart.ts @@ -32,7 +32,11 @@ import { } from "../../data/recorder"; import type { HomeAssistant } from "../../types"; import "./ha-chart-base"; -import type { ChartResizeOptions, HaChartBase } from "./ha-chart-base"; +import type { + ChartResizeOptions, + ChartDatasetExtra, + HaChartBase, +} from "./ha-chart-base"; export const supportedStatTypeMap: Record = { mean: "mean", @@ -79,10 +83,14 @@ export class StatisticsChart extends LitElement { @state() private _chartData: ChartData = { datasets: [] }; + @state() private _chartDatasetExtra: ChartDatasetExtra[] = []; + @state() private _statisticIds: string[] = []; @state() private _chartOptions?: ChartOptions; + @state() private _hiddenStats = new Set(); + @query("ha-chart-base") private _chart?: HaChartBase; private _computedStyle?: CSSStyleDeclaration; @@ -96,6 +104,9 @@ export class StatisticsChart extends LitElement { } public willUpdate(changedProps: PropertyValues) { + if (changedProps.has("legendMode")) { + this._hiddenStats.clear(); + } if ( !this.hasUpdated || changedProps.has("unit") || @@ -110,7 +121,8 @@ export class StatisticsChart extends LitElement { changedProps.has("statisticsData") || changedProps.has("statTypes") || changedProps.has("chartType") || - changedProps.has("hideLegend") + changedProps.has("hideLegend") || + changedProps.has("_hiddenStats") ) { this._generateData(); } @@ -145,14 +157,30 @@ export class StatisticsChart extends LitElement { return html` `; } + private _datasetHidden(ev) { + ev.stopPropagation(); + this._hiddenStats.add(this._statisticIds[ev.detail.index]); + this.requestUpdate("_hiddenStats"); + } + + private _datasetUnhidden(ev) { + ev.stopPropagation(); + this._hiddenStats.delete(this._statisticIds[ev.detail.index]); + this.requestUpdate("_hiddenStats"); + } + private _createOptions(unit?: string) { this._chartOptions = { parsing: false, @@ -274,6 +302,7 @@ export class StatisticsChart extends LitElement { let colorIndex = 0; const statisticsData = Object.entries(this.statisticsData); const totalDataSets: ChartDataset<"line">[] = []; + const totalDatasetExtras: ChartDatasetExtra[] = []; const statisticIds: string[] = []; let endTime: Date; @@ -324,6 +353,7 @@ export class StatisticsChart extends LitElement { // The datasets for the current statistic const statDataSets: ChartDataset<"line">[] = []; + const statDatasetExtras: ChartDatasetExtra[] = []; const pushData = ( start: Date, @@ -384,9 +414,20 @@ export class StatisticsChart extends LitElement { }) : this.statTypes; + let displayed_legend = false; sortedTypes.forEach((type) => { if (statisticsHaveType(stats, type)) { const band = drawBands && (type === "min" || type === "max"); + if (!this.hideLegend) { + const show_legend = hasMean + ? type === "mean" + : displayed_legend === false; + statDatasetExtras.push({ + legend_label: name, + show_legend, + }); + displayed_legend = displayed_legend || show_legend; + } statTypes.push(type); statDataSets.push({ label: name @@ -408,6 +449,9 @@ export class StatisticsChart extends LitElement { band && hasMean ? color + (this.hideLegend ? "00" : "7F") : color, backgroundColor: band ? color + "3F" : color + "7F", pointRadius: 0, + hidden: !this.hideLegend + ? this._hiddenStats.has(statistic_id) + : false, data: [], // @ts-ignore unit: meta?.unit_of_measurement, @@ -446,6 +490,7 @@ export class StatisticsChart extends LitElement { // Concat two arrays Array.prototype.push.apply(totalDataSets, statDataSets); + Array.prototype.push.apply(totalDatasetExtras, statDatasetExtras); }); if (unit) { @@ -455,6 +500,7 @@ export class StatisticsChart extends LitElement { this._chartData = { datasets: totalDataSets, }; + this._chartDatasetExtra = totalDatasetExtras; this._statisticIds = statisticIds; } From 2524c96db69d80641a3aa8b715c64453d096a86d Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:03:35 -0500 Subject: [PATCH 045/116] Localize system log detail (#19813) --- .../config/logs/dialog-system-log-detail.ts | 30 ++++++++++++++----- src/translations/en.json | 12 +++++++- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/panels/config/logs/dialog-system-log-detail.ts b/src/panels/config/logs/dialog-system-log-detail.ts index 5e047d7b07..1ea09ae743 100644 --- a/src/panels/config/logs/dialog-system-log-detail.ts +++ b/src/panels/config/logs/dialog-system-log-detail.ts @@ -102,12 +102,17 @@ class DialogSystemLogDetail extends LitElement { : ""}

- Logger: ${item.name}
- Source: ${item.source.join(":")} + ${this.hass.localize("ui.panel.config.logs.detail.logger")}: + ${item.name}
+ ${this.hass.localize("ui.panel.config.logs.detail.source")}: + ${item.source.join(":")} ${integration ? html`
- Integration: ${domainToName(this.hass.localize, integration)} + ${this.hass.localize( + "ui.panel.config.logs.detail.integration" + )}: + ${domainToName(this.hass.localize, integration)} ${!this._manifest || // Can happen with custom integrations !showDocumentation @@ -122,7 +127,9 @@ class DialogSystemLogDetail extends LitElement { : this._manifest.documentation} target="_blank" rel="noreferrer" - >documentation${this.hass.localize( + "ui.panel.config.logs.detail.documentation" + )}${this._manifest.is_built_in || this._manifest.issue_tracker ? html`, @@ -133,7 +140,9 @@ class DialogSystemLogDetail extends LitElement { )} target="_blank" rel="noreferrer" - >issues${this.hass.localize( + "ui.panel.config.logs.detail.issues" + )}` : ""}) `} @@ -142,16 +151,21 @@ class DialogSystemLogDetail extends LitElement {
${item.count > 0 ? html` - First occurred: + ${this.hass.localize( + "ui.panel.config.logs.detail.first_occurred" + )}: ${formatSystemLogTime( item.first_occurred, this.hass!.locale, this.hass!.config )} - (${item.count} occurrences)
+ (${item.count} + ${this.hass.localize( + "ui.panel.config.logs.detail.occurrences" + )})
` : ""} - Last logged: + ${this.hass.localize("ui.panel.config.logs.detail.last_logged")}: ${formatSystemLogTime( item.timestamp, this.hass!.locale, diff --git a/src/translations/en.json b/src/translations/en.json index ae81ca6a75..12431163c6 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2169,7 +2169,17 @@ "show_full_logs": "Show full logs", "download_full_log": "Download full log", "provider_not_found": "Log provider not found", - "provider_not_available": "Logs for ''{provider}'' are not available on your system." + "provider_not_available": "Logs for ''{provider}'' are not available on your system.", + "detail": { + "logger": "Logger", + "source": "Source", + "integration": "[%key:ui::panel::config::integrations::integration%]", + "documentation": "documentation", + "issues": "issues", + "first_occurred": "First occurred", + "occurrences": "occurrences", + "last_logged": "Last logged" + } }, "lovelace": { "caption": "Dashboards", From 8f984517bb13baa9895e70dcca83a4ea0cefbe34 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:05:05 -0500 Subject: [PATCH 046/116] Update typescript-eslint monorepo to v7 (major) (#19810) Update typescript-eslint monorepo to v7 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 114 +++++++++++++++++++++++++-------------------------- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index ad4b0b90a6..635042774f 100644 --- a/package.json +++ b/package.json @@ -183,8 +183,8 @@ "@types/tar": "6.1.11", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", - "@typescript-eslint/eslint-plugin": "6.21.0", - "@typescript-eslint/parser": "6.21.0", + "@typescript-eslint/eslint-plugin": "7.0.1", + "@typescript-eslint/parser": "7.0.1", "@web/dev-server": "0.1.38", "@web/dev-server-rollup": "0.4.1", "babel-loader": "9.1.3", diff --git a/yarn.lock b/yarn.lock index 8766ab76a6..e2484f3833 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4524,15 +4524,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.21.0" +"@typescript-eslint/eslint-plugin@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/eslint-plugin@npm:7.0.1" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/type-utils": "npm:6.21.0" - "@typescript-eslint/utils": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" + "@typescript-eslint/scope-manager": "npm:7.0.1" + "@typescript-eslint/type-utils": "npm:7.0.1" + "@typescript-eslint/utils": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -4540,73 +4540,73 @@ __metadata: semver: "npm:^7.5.4" ts-api-utils: "npm:^1.0.1" peerDependencies: - "@typescript-eslint/parser": ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 + "@typescript-eslint/parser": ^7.0.0 + eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/a57de0f630789330204cc1531f86cfc68b391cafb1ba67c8992133f1baa2a09d629df66e71260b040de4c9a3ff1252952037093c4128b0d56c4dbb37720b4c1d + checksum: 10/0862e8ec8677fcea794394fc9eab8dba11043c08452722790e0d296d4ee84713180676e1e3135be4203ace7bb73933c94159255cb9190c7bc13bf7f03a361915 languageName: node linkType: hard -"@typescript-eslint/parser@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/parser@npm:6.21.0" +"@typescript-eslint/parser@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/parser@npm:7.0.1" dependencies: - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/typescript-estree": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" + "@typescript-eslint/scope-manager": "npm:7.0.1" + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/typescript-estree": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" debug: "npm:^4.3.4" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/4d51cdbc170e72275efc5ef5fce48a81ec431e4edde8374f4d0213d8d370a06823e1a61ae31d502a5f1b0d1f48fc4d29a1b1b5c2dcf809d66d3872ccf6e46ac7 + checksum: 10/b4ba1743ab730268a1924139f072e4a0a56959526fb6377e1b3964518b6c6851733ae446a44d29fed1cb96669e2913cca524895ce77a6205aaed8bda00e8cd5d languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/scope-manager@npm:6.21.0" +"@typescript-eslint/scope-manager@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/scope-manager@npm:7.0.1" dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" - checksum: 10/fe91ac52ca8e09356a71dc1a2f2c326480f3cccfec6b2b6d9154c1a90651ab8ea270b07c67df5678956c3bbf0bbe7113ab68f68f21b20912ea528b1214197395 + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" + checksum: 10/dade6055bb853adb54de795cc3da5ab8550236d4186f108573fdb02e636ab7fc4300a55b506698ced4087ca43b143a5593931cb3195ab4790470b456d9ff8846 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/type-utils@npm:6.21.0" +"@typescript-eslint/type-utils@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/type-utils@npm:7.0.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.21.0" - "@typescript-eslint/utils": "npm:6.21.0" + "@typescript-eslint/typescript-estree": "npm:7.0.1" + "@typescript-eslint/utils": "npm:7.0.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/d03fb3ee1caa71f3ce053505f1866268d7ed79ffb7fed18623f4a1253f5b8f2ffc92636d6fd08fcbaf5bd265a6de77bf192c53105131e4724643dfc910d705fc + checksum: 10/cf20a3c0e56121ac62467e48121e135798db6d2999bd4f96ed44edc39f2597812d12b1bd6a378adec54d6c5e7db75fa5f98a27ce399792a2c8a5bbd3649952f7 languageName: node linkType: hard -"@typescript-eslint/types@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/types@npm:6.21.0" - checksum: 10/e26da86d6f36ca5b6ef6322619f8ec55aabcd7d43c840c977ae13ae2c964c3091fc92eb33730d8be08927c9de38466c5323e78bfb270a9ff1d3611fe821046c5 +"@typescript-eslint/types@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/types@npm:7.0.1" + checksum: 10/c08b2d34bab2a877a45a1e4c2923f50d03022b682b7aaba929ae2a9a5ad32db0e46265544a6616ccb98654b434250621be0e282fc5b21b8ccaf6b78741d68f67 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" +"@typescript-eslint/typescript-estree@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/typescript-estree@npm:7.0.1" dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/visitor-keys": "npm:7.0.1" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -4616,34 +4616,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/b32fa35fca2a229e0f5f06793e5359ff9269f63e9705e858df95d55ca2cd7fdb5b3e75b284095a992c48c5fc46a1431a1a4b6747ede2dd08929dc1cbacc589b8 + checksum: 10/b0b0adc84502d1ffcf3a0024179e0f2780be5f8b0a18328db46d430efc4e38a7965656b4392dd47d6176bbb1ee200aec6dd8581c39b606e260750574358cde9f languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/utils@npm:6.21.0" +"@typescript-eslint/utils@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/utils@npm:7.0.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/typescript-estree": "npm:6.21.0" + "@typescript-eslint/scope-manager": "npm:7.0.1" + "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/typescript-estree": "npm:7.0.1" semver: "npm:^7.5.4" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: 10/b404a2c55a425a79d054346ae123087d30c7ecf7ed7abcf680c47bf70c1de4fabadc63434f3f460b2fa63df76bc9e4a0b9fa2383bb8a9fcd62733fb5c4e4f3e3 + eslint: ^8.56.0 + checksum: 10/b7e0cb2994f73b3f416684dc175d4e1da5f8306d6c81abbad2f219fa3e4f29154063a3c9568e4a1f879a38b79c62250e596e4ed7265f7bd1ed9b3db806cb92b7 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" +"@typescript-eslint/visitor-keys@npm:7.0.1": + version: 7.0.1 + resolution: "@typescript-eslint/visitor-keys@npm:7.0.1" dependencies: - "@typescript-eslint/types": "npm:6.21.0" + "@typescript-eslint/types": "npm:7.0.1" eslint-visitor-keys: "npm:^3.4.1" - checksum: 10/30422cdc1e2ffad203df40351a031254b272f9c6f2b7e02e9bfa39e3fc2c7b1c6130333b0057412968deda17a3a68a578a78929a8139c6acef44d9d841dc72e1 + checksum: 10/915c5b19302a4c76e843cd2d04a9a2b11907e658d7018c8b55c338b090d9115d3719809aa05b8af130cc1b216c77626d210c20f705b732e83d04ceae0c112f6b languageName: node linkType: hard @@ -9541,8 +9541,8 @@ __metadata: "@types/tar": "npm:6.1.11" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@typescript-eslint/eslint-plugin": "npm:6.21.0" - "@typescript-eslint/parser": "npm:6.21.0" + "@typescript-eslint/eslint-plugin": "npm:7.0.1" + "@typescript-eslint/parser": "npm:7.0.1" "@vaadin/combo-box": "npm:24.3.6" "@vaadin/vaadin-themable-mixin": "npm:24.3.6" "@vibrant/color": "npm:3.2.1-alpha.1" From 7050453783fc65557adb57f76a6f29d8423f33bc Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 16 Feb 2024 17:13:43 +0100 Subject: [PATCH 047/116] Check active matter fabric by index (#19692) Check active fabric by id --- src/data/matter.ts | 1 + .../matter/dialog-matter-manage-fabrics.ts | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/data/matter.ts b/src/data/matter.ts index 5b4009178d..d6c4de87a0 100644 --- a/src/data/matter.ts +++ b/src/data/matter.ts @@ -35,6 +35,7 @@ export interface MatterNodeDiagnostics { mac_address?: string; available: boolean; active_fabrics: MatterFabricData[]; + active_fabric_index: number; } export interface MatterPingResult { diff --git a/src/panels/config/integrations/integration-panels/matter/dialog-matter-manage-fabrics.ts b/src/panels/config/integrations/integration-panels/matter/dialog-matter-manage-fabrics.ts index f10c634be9..21cf8ed491 100644 --- a/src/panels/config/integrations/integration-panels/matter/dialog-matter-manage-fabrics.ts +++ b/src/panels/config/integrations/integration-panels/matter/dialog-matter-manage-fabrics.ts @@ -20,8 +20,6 @@ import { haStyleDialog } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; import { MatterManageFabricsDialogParams } from "./show-dialog-matter-manage-fabrics"; -const NABUCASA_FABRIC = 4939; - @customElement("dialog-matter-manage-fabrics") class DialogMatterManageFabrics extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -61,8 +59,9 @@ class DialogMatterManageFabrics extends LitElement { (fabric) => html`${fabric.vendor_name || fabric.fabric_label || fabric.vendor_id} @@ -99,6 +98,9 @@ class DialogMatterManageFabrics extends LitElement { private async _removeFabric(ev) { const fabric: MatterFabricData = ev.target.fabric; + if (this._nodeDiagnostics!.active_fabric_index === fabric.fabric_index) { + return; + } const fabricName = fabric.vendor_name || fabric.fabric_label || fabric.vendor_id.toString(); const confirm = await showConfirmationDialog(this, { From 4ba4a28aa02f3ee67255383bc8fcb0600ba277df Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Fri, 16 Feb 2024 18:41:53 +0100 Subject: [PATCH 048/116] Bring back backend translations in devcontainer (#19793) * Bring back translations * Apply feedback Co-authored-by: Steve Repsher --------- Co-authored-by: Steve Repsher --- script/core | 1 + 1 file changed, 1 insertion(+) diff --git a/script/core b/script/core index 6ba6331de2..c9848c4625 100755 --- a/script/core +++ b/script/core @@ -40,6 +40,7 @@ if [ -n "$ref" ]; then echo "Installing Home Assistant core at ${ref}..." python3 -m pip install --user --upgrade --src "$HOME/src" \ --editable "git+${coreURL}@${ref}#egg=homeassistant" + (cd ~/src/homeassistant && exec python3 -m script.translations develop --all) fi if [ ! -d "${WD}/config" ]; then From add2dedc7f5fc516b0552b2ad5e609f8af7084a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 20:30:51 -0500 Subject: [PATCH 049/116] Update dependency hls.js to v1.5.5 (#19817) --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 635042774f..2f5aabec9e 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "element-internals-polyfill": "1.3.10", "fuse.js": "7.0.0", "google-timezones-json": "1.2.0", - "hls.js": "1.5.4", + "hls.js": "1.5.5", "home-assistant-js-websocket": "9.1.0", "idb-keyval": "6.2.1", "intl-messageformat": "10.5.11", diff --git a/yarn.lock b/yarn.lock index e2484f3833..4299c399a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9432,10 +9432,10 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:1.5.4": - version: 1.5.4 - resolution: "hls.js@npm:1.5.4" - checksum: 10/b86ee818db7a9b3f576fa1fb6c325a91885d6018be6e6cf496a7e1e8fd9ae060979de4347fd6635af47e704170cb47a09ef0942a38409c165f9958044d232e4d +"hls.js@npm:1.5.5": + version: 1.5.5 + resolution: "hls.js@npm:1.5.5" + checksum: 10/f182f9d1819527b76cf493646ce9a5cffaa9bb0fae02eab446fb16063fe15c527655a1237ba7f2ba3a1c51d11c64c25fb2cfe01ad94980038a810dc2ea982340 languageName: node linkType: hard @@ -9589,7 +9589,7 @@ __metadata: gulp-merge-json: "npm:2.1.2" gulp-rename: "npm:2.0.0" gulp-zopfli-green: "npm:6.0.1" - hls.js: "npm:1.5.4" + hls.js: "npm:1.5.5" home-assistant-js-websocket: "npm:9.1.0" html-minifier-terser: "npm:7.2.0" husky: "npm:9.0.10" From ea642515c1e0188640bfd342cf97f796b2ccfded Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 17 Feb 2024 13:06:30 -0500 Subject: [PATCH 050/116] Update dependency husky to v9.0.11 (#19818) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 2f5aabec9e..4cc75ecf9c 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ "gulp-rename": "2.0.0", "gulp-zopfli-green": "6.0.1", "html-minifier-terser": "7.2.0", - "husky": "9.0.10", + "husky": "9.0.11", "instant-mocha": "1.5.2", "jszip": "3.10.1", "lint-staged": "15.2.2", diff --git a/yarn.lock b/yarn.lock index 4299c399a9..ae3d510671 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9592,7 +9592,7 @@ __metadata: hls.js: "npm:1.5.5" home-assistant-js-websocket: "npm:9.1.0" html-minifier-terser: "npm:7.2.0" - husky: "npm:9.0.10" + husky: "npm:9.0.11" idb-keyval: "npm:6.2.1" instant-mocha: "npm:1.5.2" intl-messageformat: "npm:10.5.11" @@ -9874,12 +9874,12 @@ __metadata: languageName: node linkType: hard -"husky@npm:9.0.10": - version: 9.0.10 - resolution: "husky@npm:9.0.10" +"husky@npm:9.0.11": + version: 9.0.11 + resolution: "husky@npm:9.0.11" bin: husky: bin.mjs - checksum: 10/c303f1862e2b63873605df55a2b08303155e35c799585d7dd677628f62d716e7304bd984fc7d00ec44e740caac07d51720d1a0abb0a23a70a38859d89eb8e72d + checksum: 10/8a9b7cb9dc8494b470b3b47b386e65d579608c6206da80d3cc8b71d10e37947264af3dfe00092368dad9673b51d2a5ee87afb4b2291e77ba9e7ec1ac36e56cd1 languageName: node linkType: hard From 9a9fbda08b87c1f428f296975c70e332fc635c69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 17 Feb 2024 13:09:59 -0500 Subject: [PATCH 051/116] Update dependency core-js to v3.36.0 (#19822) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 4cc75ecf9c..69eeb276de 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "app-datepicker": "5.1.1", "chart.js": "4.4.1", "comlink": "4.4.1", - "core-js": "3.35.1", + "core-js": "3.36.0", "cropperjs": "1.6.1", "date-fns": "2.30.0", "date-fns-tz": "2.0.0", diff --git a/yarn.lock b/yarn.lock index ae3d510671..18a64a6f85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6977,10 +6977,10 @@ __metadata: languageName: node linkType: hard -"core-js@npm:3.35.1": - version: 3.35.1 - resolution: "core-js@npm:3.35.1" - checksum: 10/5d31f22eb05cf66bd1a2088a04b7106faa5d0b91c1ffa5d72c5203e4974c31bd7e11969297f540a806c00c74c23991eaad5639592df8b5dbe4412fff3c075cd5 +"core-js@npm:3.36.0": + version: 3.36.0 + resolution: "core-js@npm:3.36.0" + checksum: 10/896326c6391c1607dc645293c214cd31c6c535d4a77a88b15fc29e787199f9b06dc15986ddfbc798335bf7a7afd1e92152c94aa5a974790a7f97a98121774302 languageName: node linkType: hard @@ -9559,7 +9559,7 @@ __metadata: chai: "npm:5.1.0" chart.js: "npm:4.4.1" comlink: "npm:4.4.1" - core-js: "npm:3.35.1" + core-js: "npm:3.36.0" cropperjs: "npm:1.6.1" date-fns: "npm:2.30.0" date-fns-tz: "npm:2.0.0" From 4de3db52cb2b65657c229dbd56bfe9ec38c8727e Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Sun, 18 Feb 2024 02:33:50 +0100 Subject: [PATCH 052/116] Remove cancel button on edit view dialog (#19821) --- src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts b/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts index a16f8237f4..22583a3595 100644 --- a/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts +++ b/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts @@ -313,9 +313,6 @@ export class HuiDialogEditView extends LitElement { ` : ""} - ${this.hass!.localize("ui.common.cancel")} Date: Sun, 18 Feb 2024 00:42:48 -0500 Subject: [PATCH 053/116] Update dependency @bundle-stats/plugin-webpack-filter to v4.10.0 (#19825) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 69eeb276de..6c0677564e 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "@babel/plugin-transform-runtime": "7.23.9", "@babel/preset-env": "7.23.9", "@babel/preset-typescript": "7.23.3", - "@bundle-stats/plugin-webpack-filter": "4.9.2", + "@bundle-stats/plugin-webpack-filter": "4.10.0", "@koa/cors": "5.0.0", "@lokalise/node-api": "12.1.0", "@octokit/auth-oauth-device": "6.0.1", diff --git a/yarn.lock b/yarn.lock index 18a64a6f85..ae22397052 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1447,12 +1447,12 @@ __metadata: languageName: node linkType: hard -"@bundle-stats/plugin-webpack-filter@npm:4.9.2": - version: 4.9.2 - resolution: "@bundle-stats/plugin-webpack-filter@npm:4.9.2" +"@bundle-stats/plugin-webpack-filter@npm:4.10.0": + version: 4.10.0 + resolution: "@bundle-stats/plugin-webpack-filter@npm:4.10.0" peerDependencies: core-js: ^3.0.0 - checksum: 10/d57aeff530a1af099929f67d12402f5a563e018333d86de159af2c1714274b040de9e1a9f6dad1b3bbee6b7af0cde6f9668a368138f27ac13d34642beba8e2f0 + checksum: 10/43964bce792eab1e62571241278c71148b51513d262516823b7a8ba18c73da64e77cac2d93cd969db64f6699bad11bfb8f7d49de363bf4f9248b76e97479efcd languageName: node linkType: hard @@ -9451,7 +9451,7 @@ __metadata: "@babel/preset-typescript": "npm:7.23.3" "@babel/runtime": "npm:7.23.9" "@braintree/sanitize-url": "npm:7.0.0" - "@bundle-stats/plugin-webpack-filter": "npm:4.9.2" + "@bundle-stats/plugin-webpack-filter": "npm:4.10.0" "@codemirror/autocomplete": "npm:6.12.0" "@codemirror/commands": "npm:6.3.3" "@codemirror/language": "npm:6.10.1" From 63603a281e07288aa6a1f2f599f8d28efc453e42 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 16:01:25 -0500 Subject: [PATCH 054/116] Update dependency webpack to v5.90.2 (#19828) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 6c0677564e..446ea474d9 100644 --- a/package.json +++ b/package.json @@ -240,7 +240,7 @@ "typescript": "5.3.3", "vinyl-buffer": "1.0.1", "vinyl-source-stream": "2.0.0", - "webpack": "5.90.1", + "webpack": "5.90.2", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.1", "webpack-manifest-plugin": "5.0.0", diff --git a/yarn.lock b/yarn.lock index ae22397052..631ab800ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9648,7 +9648,7 @@ __metadata: vis-network: "npm:9.1.9" vue: "npm:2.7.16" vue2-daterange-picker: "npm:0.6.8" - webpack: "npm:5.90.1" + webpack: "npm:5.90.2" webpack-cli: "npm:5.1.4" webpack-dev-server: "npm:4.15.1" webpack-manifest-plugin: "npm:5.0.0" @@ -16157,9 +16157,9 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.90.1": - version: 5.90.1 - resolution: "webpack@npm:5.90.1" +"webpack@npm:5.90.2": + version: 5.90.2 + resolution: "webpack@npm:5.90.2" dependencies: "@types/eslint-scope": "npm:^3.7.3" "@types/estree": "npm:^1.0.5" @@ -16190,7 +16190,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 10/6ad23518123f1742238177920cefa61152d981f986adac5901236845c86ba9bb375a3ba75e188925c856c3d2a76a2ba119e95b8a608a51424968389041089075 + checksum: 10/4eaeed1255c9c7738921c4ce4facdb3b78dbfcb3441496942f6d160a41fbcebd24fb2c6dbb64739b357c5ff78e5a298f6c82eca482438b95130a3ba4e16d084a languageName: node linkType: hard From 86626b185558934da56e1b025fac99ba591b8b83 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Mon, 19 Feb 2024 04:06:33 -0500 Subject: [PATCH 055/116] Localize Long Lived Token Dialog (#19830) --- src/panels/profile/ha-long-lived-access-token-dialog.ts | 4 +++- src/translations/en.json | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/panels/profile/ha-long-lived-access-token-dialog.ts b/src/panels/profile/ha-long-lived-access-token-dialog.ts index 4dd9fe1298..904680a2d1 100644 --- a/src/panels/profile/ha-long-lived-access-token-dialog.ts +++ b/src/panels/profile/ha-long-lived-access-token-dialog.ts @@ -74,7 +74,9 @@ export class HaLongLivedAccessTokenDialog extends LitElement { ? this._qrCode : html` - Generate QR code + ${this.hass.localize( + "ui.panel.profile.long_lived_access_tokens.generate_qr_code" + )} `}

diff --git a/src/translations/en.json b/src/translations/en.json index 12431163c6..d0cda35a16 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5985,7 +5985,8 @@ "prompt_name": "Give the token a name", "prompt_copy_token": "Copy your access token. It will not be shown again.", "empty_state": "You have no long-lived access tokens yet.", - "qr_code_image": "QR code for token {name}" + "qr_code_image": "QR code for token {name}", + "generate_qr_code": "Generate QR Code" } }, "todo": { From 50cf6d2af9959e1eaa09fd5af04b513f2eb81321 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 19 Feb 2024 12:34:52 -0500 Subject: [PATCH 056/116] Lock file maintenance (#19832) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 711 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 397 insertions(+), 314 deletions(-) diff --git a/yarn.lock b/yarn.lock index 631ab800ec..29f4b6d40e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -129,8 +129,8 @@ __metadata: linkType: hard "@babel/helper-create-class-features-plugin@npm:^7.22.15, @babel/helper-create-class-features-plugin@npm:^7.23.6, @babel/helper-create-class-features-plugin@npm:^7.23.9": - version: 7.23.9 - resolution: "@babel/helper-create-class-features-plugin@npm:7.23.9" + version: 7.23.10 + resolution: "@babel/helper-create-class-features-plugin@npm:7.23.10" dependencies: "@babel/helper-annotate-as-pure": "npm:^7.22.5" "@babel/helper-environment-visitor": "npm:^7.22.20" @@ -143,7 +143,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0 - checksum: 10/91c8aa8888780bd90aa50f511917cb0953ccd61b2ea4abf61915c1d68d99bb14b472969a8ae5b391d7890759dfc22be79104297be07919c38351714a4ce2fe74 + checksum: 10/8b9f02526eeb03ef1d2bc89e3554377ae966b33a74078ab1f88168dfa725dc206ea5ecf4cf417c3651d8a6b3c70204f6939a9aa0401be3d0d32ddbf6024ea3c7 languageName: node linkType: hard @@ -1999,9 +1999,9 @@ __metadata: linkType: hard "@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": - version: 3.1.1 - resolution: "@jridgewell/resolve-uri@npm:3.1.1" - checksum: 10/64d59df8ae1a4e74315eb1b61e012f1c7bc8aac47a3a1e683f6fe7008eab07bc512a742b7aa7c0405685d1421206de58c9c2e6adbfe23832f8bd69408ffc183e + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10/97106439d750a409c22c8bff822d648f6a71f3aa9bc8e5129efdc36343cd3096ddc4eeb1c62d2fe48e9bdd4db37b05d4646a17114ecebd3bbcacfa2de51c3c1d languageName: node linkType: hard @@ -2040,12 +2040,12 @@ __metadata: linkType: hard "@jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.21 - resolution: "@jridgewell/trace-mapping@npm:0.3.21" + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" dependencies: "@jridgewell/resolve-uri": "npm:^3.1.0" "@jridgewell/sourcemap-codec": "npm:^1.4.14" - checksum: 10/925dda0620887e5a24f11b5a3a106f4e8b1a66155b49be6ceee61432174df33a17c243d8a89b2cd79ccebd281d817878759236a2fc42c47325ae9f73dfbfb90d + checksum: 10/48d3e3db00dbecb211613649a1849876ba5544a3f41cf5e6b99ea1130272d6cf18591b5b67389bce20f1c871b4ede5900c3b6446a7aab6d0a3b2fe806a834db7 languageName: node linkType: hard @@ -2089,11 +2089,11 @@ __metadata: linkType: hard "@lezer/lr@npm:^1.0.0": - version: 1.3.14 - resolution: "@lezer/lr@npm:1.3.14" + version: 1.4.0 + resolution: "@lezer/lr@npm:1.4.0" dependencies: "@lezer/common": "npm:^1.0.0" - checksum: 10/9d32701f91fdf7d570073f5e83cda028c80bea7633f928c809eb6977d4f0b5e32424f95fb78cafea98789c4c0dadc9694636903290f0c1c418d2a47ed7f18f46 + checksum: 10/7391d0d08e54cd9e4f4d46e6ee6aa81fbaf079b22ed9c13d01fc9928e0ffd16d0c2d21b2cedd55675ad6c687277db28349ea8db81c9c69222cd7e7c40edd026e languageName: node linkType: hard @@ -2126,9 +2126,9 @@ __metadata: linkType: hard "@lit-labs/ssr-dom-shim@npm:^1.0.0, @lit-labs/ssr-dom-shim@npm:^1.1.0": - version: 1.1.2 - resolution: "@lit-labs/ssr-dom-shim@npm:1.1.2" - checksum: 10/a930f7de57b952dc21317a5754aa0411e000bb4991053cde771c111b7792c4a4cdc896922f0353c832215bed71400431c5ab5a6252c8f4f70bb9ce0b37fe4752 + version: 1.2.0 + resolution: "@lit-labs/ssr-dom-shim@npm:1.2.0" + checksum: 10/33679defe08538ac6fb612854e7d32b4ea1e787cceba2c3373d26fd56baa9833881887da7bade3930a176ba518dc00bb42ce95d82ddb6af6b05b8fbe1fc3169f languageName: node linkType: hard @@ -3193,15 +3193,15 @@ __metadata: linkType: hard "@npmcli/agent@npm:^2.0.0": - version: 2.2.0 - resolution: "@npmcli/agent@npm:2.2.0" + version: 2.2.1 + resolution: "@npmcli/agent@npm:2.2.1" dependencies: agent-base: "npm:^7.1.0" http-proxy-agent: "npm:^7.0.0" https-proxy-agent: "npm:^7.0.1" lru-cache: "npm:^10.0.1" socks-proxy-agent: "npm:^8.0.1" - checksum: 10/822ea077553cd9cfc5cbd6d92380b0950fcb054a7027cd1b63a33bd0cbb16b0c6626ea75d95ec0e804643c8904472d3361d2da8c2444b1fb02a9b525d9c07c41 + checksum: 10/d4a48128f61e47f2f5c89315a5350e265dc619987e635bd62b52b29c7ed93536e724e721418c0ce352ceece86c13043c67aba1b70c3f5cc72fce6bb746706162 languageName: node linkType: hard @@ -3234,8 +3234,8 @@ __metadata: linkType: hard "@octokit/core@npm:^5.0.0": - version: 5.0.2 - resolution: "@octokit/core@npm:5.0.2" + version: 5.1.0 + resolution: "@octokit/core@npm:5.1.0" dependencies: "@octokit/auth-token": "npm:^4.0.0" "@octokit/graphql": "npm:^7.0.0" @@ -3244,7 +3244,7 @@ __metadata: "@octokit/types": "npm:^12.0.0" before-after-hook: "npm:^2.2.0" universal-user-agent: "npm:^6.0.0" - checksum: 10/bb991f88793fab043c4c09f9441432596fe0e6448caf42cd2209f52c1f26807418be488ad2cea7a8293e58e79e5c0019f38dda46e8cf96af5e89e43cca37ec3e + checksum: 10/8062e86a3088f24a691b36d2c3e9f33e864cefcb5f544b0633650358bce280708b111551cbe855ecf6a5190d6fc4fec1220117c329a2c27525940dd97b868614 languageName: node linkType: hard @@ -3317,13 +3317,13 @@ __metadata: linkType: hard "@octokit/plugin-rest-endpoint-methods@npm:^10.0.0": - version: 10.2.0 - resolution: "@octokit/plugin-rest-endpoint-methods@npm:10.2.0" + version: 10.3.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:10.3.0" dependencies: - "@octokit/types": "npm:^12.3.0" + "@octokit/types": "npm:^12.4.0" peerDependencies: "@octokit/core": ">=5" - checksum: 10/0f8ca73b3e582b366b400278f19df6309f263efa3809a9d6ba613063e7a26f16d6f8d69c413bf9b23c2431ad4c795e4e06a43717b6acc1367186fb55347cfb69 + checksum: 10/be202aca31a513e9e06eda680ae761d05a944a2900523c2041a8ac6bc43e7a5bab0f76bcba09ef96ed48e3b1fabe4104a174b2f839dfb7a2a6f3d632023b4c6a languageName: node linkType: hard @@ -3352,14 +3352,14 @@ __metadata: linkType: hard "@octokit/request@npm:^8.0.0, @octokit/request@npm:^8.0.1, @octokit/request@npm:^8.0.2": - version: 8.1.6 - resolution: "@octokit/request@npm:8.1.6" + version: 8.2.0 + resolution: "@octokit/request@npm:8.2.0" dependencies: "@octokit/endpoint": "npm:^9.0.0" "@octokit/request-error": "npm:^5.0.0" "@octokit/types": "npm:^12.0.0" universal-user-agent: "npm:^6.0.0" - checksum: 10/aebea1c33d607d23c70f663cd5f8279a8bd932ab77b4ca5cca3b33968a347b4adb47476c886086f3a9aa1acefab3b79adac78ee7aa2dacd67eb1f2a05e272618 + checksum: 10/553ba8b99ea6fe2d3b66b2be6df06f7fe62a0b81e441d334ef25388cf0fcdab4a888fcfe2c1fb1ada262c233bcf7169da3ad5e03f024060ea5723f7753b0de3e languageName: node linkType: hard @@ -3375,12 +3375,12 @@ __metadata: languageName: node linkType: hard -"@octokit/types@npm:^12.0.0, @octokit/types@npm:^12.3.0, @octokit/types@npm:^12.4.0": - version: 12.4.0 - resolution: "@octokit/types@npm:12.4.0" +"@octokit/types@npm:^12.0.0, @octokit/types@npm:^12.4.0": + version: 12.5.0 + resolution: "@octokit/types@npm:12.5.0" dependencies: "@octokit/openapi-types": "npm:^19.1.0" - checksum: 10/b0a893e31fed59a919c2072ae67b671aa5f21e00ee3dee689af325f09f12ddd9175ce07c590b835d183bcb1cd2a2da908e02391b2fc33071881561366b2a35e7 + checksum: 10/a2c8e736e368c781b9389a41fe4ec2cf81563674cf8d74bcc28bf1901dfc4b0c092101c7b333309933b8c14a5a3d1de9ca7eaec93451c41b708b4936e0960894 languageName: node linkType: hard @@ -3878,11 +3878,11 @@ __metadata: linkType: hard "@sinonjs/commons@npm:^3.0.0": - version: 3.0.0 - resolution: "@sinonjs/commons@npm:3.0.0" + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" dependencies: type-detect: "npm:4.0.8" - checksum: 10/086720ae0bc370829322df32612205141cdd44e592a8a9ca97197571f8f970352ea39d3bda75b347c43789013ddab36b34b59e40380a49bdae1c2df3aa85fe4f + checksum: 10/a0af217ba7044426c78df52c23cedede6daf377586f3ac58857c565769358ab1f44ebf95ba04bbe38814fba6e316ca6f02870a009328294fc2c555d0f85a7117 languageName: node linkType: hard @@ -3978,12 +3978,12 @@ __metadata: linkType: hard "@types/chrome@npm:*": - version: 0.0.258 - resolution: "@types/chrome@npm:0.0.258" + version: 0.0.260 + resolution: "@types/chrome@npm:0.0.260" dependencies: "@types/filesystem": "npm:*" "@types/har-format": "npm:*" - checksum: 10/301f60ba009832f8eff232159234a4b73db2d7925eae1b67a1a4687da80d56022430bc722e8c4effc0a12c1868c0bb380e981ef8b6ba744bbe52bf47591a8198 + checksum: 10/4e7d6e5e3bd67bc481c6524aacce902568ba8f78b7da3fe1f2999f8f01863240847f1b05a358d38dbd2afdbe112dfd9ff84912a664c40595e9e4266eef3891cc languageName: node linkType: hard @@ -4037,14 +4037,14 @@ __metadata: linkType: hard "@types/cookies@npm:*": - version: 0.7.10 - resolution: "@types/cookies@npm:0.7.10" + version: 0.9.0 + resolution: "@types/cookies@npm:0.9.0" dependencies: "@types/connect": "npm:*" "@types/express": "npm:*" "@types/keygrip": "npm:*" "@types/node": "npm:*" - checksum: 10/85d4b434bac9a971d8a4122d5a7c947dcaaca98fee26e90e0b792b1046da1de414dc37ea164b1693653b9b59f72c501927de90412a3a1dff2c7bdb6abadc3608 + checksum: 10/88d2106834fca85cf9dfef984e99bf4969e77d48538d8e8408a29679b4d1f675fe4725d35f2e38d252a336b76d14a2bc84bcb34edc72238a7a8261c0808c7c56 languageName: node linkType: hard @@ -4083,14 +4083,14 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": - version: 4.17.41 - resolution: "@types/express-serve-static-core@npm:4.17.41" + version: 4.17.43 + resolution: "@types/express-serve-static-core@npm:4.17.43" dependencies: "@types/node": "npm:*" "@types/qs": "npm:*" "@types/range-parser": "npm:*" "@types/send": "npm:*" - checksum: 10/7647e19d9c3d57ddd18947d2b161b90ef0aedd15875140e5b824209be41c1084ae942d4fb43cd5f2051a6a5f8c044519ef6c9ac1b2ad86b9aa546b4f1f023303 + checksum: 10/9079e137470e0456bb8e77ae66df9505ee12591e94860bde574cfe52c5c60bbc5bf7dd44f5689c3cbb1baf0aa84442d9a21f53dcd921d18745727293cd5a5fd6 languageName: node linkType: hard @@ -4116,16 +4116,16 @@ __metadata: linkType: hard "@types/filewriter@npm:*": - version: 0.0.32 - resolution: "@types/filewriter@npm:0.0.32" - checksum: 10/fe2f19239c23c63c009c6d422227d692bc2a0cd1113f8ce31b0fb7048f32ec018003172199949843fdbb1c5988551c29e1e9e2238b9c160969b5e5edbfb76424 + version: 0.0.33 + resolution: "@types/filewriter@npm:0.0.33" + checksum: 10/495a4bb424c27eda967fe9ac3b8f7b781e6b3f9ce59403a991590cb1073022f9c5383d3c7d808ef6956b785550c36664c4fcd502dc0baf69e340bd481171e0ca languageName: node linkType: hard "@types/geojson@npm:*": - version: 7946.0.13 - resolution: "@types/geojson@npm:7946.0.13" - checksum: 10/b3b68457c89bc3f0445dc9eb54d07e6f89658672867c54989bc7f71f87d54e562195b291d43e1b84476493351271d7ccb9f5c6ab2012b29fbafbb0e8e43c4bca + version: 7946.0.14 + resolution: "@types/geojson@npm:7946.0.14" + checksum: 10/ae511bee6488ae3bd5a3a3347aedb0371e997b14225b8983679284e22fa4ebd88627c6e3ff8b08bf4cc35068cb29310c89427311ffc9322c255615821a922e71 languageName: node linkType: hard @@ -4315,11 +4315,11 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 20.11.5 - resolution: "@types/node@npm:20.11.5" + version: 20.11.19 + resolution: "@types/node@npm:20.11.19" dependencies: undici-types: "npm:~5.26.4" - checksum: 10/9f31c471047d7b3e240ce7b77ff29b0d15e83be7e3feafb3d0b0d0931122b438b1eefa302a5a2e1e9849914ff3fd76aafbd8ccb372efb1331ba048da63bce6f8 + checksum: 10/c7f4705d6c84aa21679ad180c33c13ca9567f650e66e14bcee77c7c43d14619c7cd3b4d7b2458947143030b7b1930180efa6d12d999b45366abff9fed7a17472 languageName: node linkType: hard @@ -4414,9 +4414,9 @@ __metadata: linkType: hard "@types/semver@npm:^7.5.0": - version: 7.5.6 - resolution: "@types/semver@npm:7.5.6" - checksum: 10/e77282b17f74354e17e771c0035cccb54b94cc53d0433fa7e9ba9d23fd5d7edcd14b6c8b7327d58bbd89e83b1c5eda71dfe408e06b929007e2b89586e9b63459 + version: 7.5.7 + resolution: "@types/semver@npm:7.5.7" + checksum: 10/535d88ec577fe59e38211881f79a1e2ba391e9e1516f8fff74e7196a5ba54315bace9c67a4616c334c830c89027d70a9f473a4ceb634526086a9da39180f2f9a languageName: node linkType: hard @@ -4939,9 +4939,9 @@ __metadata: linkType: hard "@vscode/web-custom-data@npm:^0.4.2": - version: 0.4.8 - resolution: "@vscode/web-custom-data@npm:0.4.8" - checksum: 10/556106530e399321d1412d0074d1a4dcfc04c17fc334df8de4f10469a2ad6f83c7245caf92174817c5bf032ec5ffd894bc418889d76a2a7d163ad0e4c910adf0 + version: 0.4.9 + resolution: "@vscode/web-custom-data@npm:0.4.9" + checksum: 10/6ccc98c43b0ead4f320dab2e33df3570590d4d3e94edf54b722c3b376fc236abb5a6e9e7655942044cf7ebe4f305a58bc58c1d8d4b251b9434d1db1266797e2e languageName: node linkType: hard @@ -5681,13 +5681,13 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.0": - version: 1.0.0 - resolution: "array-buffer-byte-length@npm:1.0.0" +"array-buffer-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "array-buffer-byte-length@npm:1.0.1" dependencies: - call-bind: "npm:^1.0.2" - is-array-buffer: "npm:^3.0.1" - checksum: 10/044e101ce150f4804ad19c51d6c4d4cfa505c5b2577bd179256e4aa3f3f6a0a5e9874c78cd428ee566ac574c8a04d7ce21af9fe52e844abfdccb82b33035a7c3 + call-bind: "npm:^1.0.5" + is-array-buffer: "npm:^3.0.4" + checksum: 10/53524e08f40867f6a9f35318fafe467c32e45e9c682ba67b11943e167344d2febc0f6977a17e699b05699e805c3e8f073d876f8bbf1b559ed494ad2cd0fae09e languageName: node linkType: hard @@ -5776,6 +5776,19 @@ __metadata: languageName: node linkType: hard +"array.prototype.filter@npm:^1.0.3": + version: 1.0.3 + resolution: "array.prototype.filter@npm:1.0.3" + dependencies: + call-bind: "npm:^1.0.2" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + es-array-method-boxes-properly: "npm:^1.0.0" + is-string: "npm:^1.0.7" + checksum: 10/3da2189afb00f95559cc73fc3c50f17a071a65bb705c0b2f2e2a2b2142781215b622442368c8b4387389b6ab251adf09ad347f9a8a4cf29d24404cc5ea1e295c + languageName: node + linkType: hard + "array.prototype.find@npm:^2.2.2": version: 2.2.2 resolution: "array.prototype.find@npm:2.2.2" @@ -5789,15 +5802,15 @@ __metadata: linkType: hard "array.prototype.findlastindex@npm:^1.2.3": - version: 1.2.3 - resolution: "array.prototype.findlastindex@npm:1.2.3" + version: 1.2.4 + resolution: "array.prototype.findlastindex@npm:1.2.4" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - es-shim-unscopables: "npm:^1.0.0" - get-intrinsic: "npm:^1.2.1" - checksum: 10/063cbab8eeac3aa01f3e980eecb9a8c5d87723032b49f7f814ecc6d75c33c03c17e3f43a458127a62e16303cab412f95d6ad9dc7e0ae6d9dc27a9bb76c24df7a + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.22.3" + es-errors: "npm:^1.3.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/12d7de8da619065b9d4c40550d11c13f2fbbc863c4270ef01d022f49ef16fbe9022441ee9d60b1e952853c661dd4b3e05c21e4348d4631c6d93ddf802a252296 languageName: node linkType: hard @@ -5825,18 +5838,19 @@ __metadata: languageName: node linkType: hard -"arraybuffer.prototype.slice@npm:^1.0.2": - version: 1.0.2 - resolution: "arraybuffer.prototype.slice@npm:1.0.2" +"arraybuffer.prototype.slice@npm:^1.0.3": + version: 1.0.3 + resolution: "arraybuffer.prototype.slice@npm:1.0.3" dependencies: - array-buffer-byte-length: "npm:^1.0.0" - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - get-intrinsic: "npm:^1.2.1" - is-array-buffer: "npm:^3.0.2" + array-buffer-byte-length: "npm:^1.0.1" + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.22.3" + es-errors: "npm:^1.2.1" + get-intrinsic: "npm:^1.2.3" + is-array-buffer: "npm:^3.0.4" is-shared-array-buffer: "npm:^1.0.2" - checksum: 10/c200faf437786f5b2c80d4564ff5481c886a16dee642ef02abdc7306c7edd523d1f01d1dd12b769c7eb42ac9bc53874510db19a92a2c035c0f6696172aafa5d3 + checksum: 10/0221f16c1e3ec7b67da870ee0e1f12b825b5f9189835392b59a22990f715827561a4f4cd5330dc7507de272d8df821be6cd4b0cb569babf5ea4be70e365a2f3d languageName: node linkType: hard @@ -5921,17 +5935,17 @@ __metadata: languageName: node linkType: hard -"available-typed-arrays@npm:^1.0.5": - version: 1.0.5 - resolution: "available-typed-arrays@npm:1.0.5" - checksum: 10/4d4d5e86ea0425696f40717882f66a570647b94ac8d273ddc7549a9b61e5da099e149bf431530ccbd776bd74e02039eb8b5edf426e3e2211ee61af16698a9064 +"available-typed-arrays@npm:^1.0.6": + version: 1.0.6 + resolution: "available-typed-arrays@npm:1.0.6" + checksum: 10/c1e2e3d3a694f21bf60e0a048d8275fa7358131a0b8e6b57714318d618b59522416db67fb9f56973af0ce596f4333ef1336ca12c37a41d5a72ef79885373a7fd languageName: node linkType: hard "axe-core@npm:^4.3.3": - version: 4.8.3 - resolution: "axe-core@npm:4.8.3" - checksum: 10/86d72bcdff867b8a4f223b910f90d1c963ddab1db3bdc5290e111453bae4e319ed14d667cd7a6ac97185ffa141261c88ac9a586aa007d007ffc19ed9ae81ee1d + version: 4.8.4 + resolution: "axe-core@npm:4.8.4" + checksum: 10/c8b3972b17afac33b9e151556fa500b9218086ca763866942de30e7f51ac50582902ad03ec16381796c95afaacfa8c33efb2d02ab4f09b6c09d6be58b95da978 languageName: node linkType: hard @@ -6199,17 +6213,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.10, browserslist@npm:^4.22.2": - version: 4.22.3 - resolution: "browserslist@npm:4.22.3" +"browserslist@npm:^4.21.10, browserslist@npm:^4.22.2, browserslist@npm:^4.22.3": + version: 4.23.0 + resolution: "browserslist@npm:4.23.0" dependencies: - caniuse-lite: "npm:^1.0.30001580" - electron-to-chromium: "npm:^1.4.648" + caniuse-lite: "npm:^1.0.30001587" + electron-to-chromium: "npm:^1.4.668" node-releases: "npm:^2.0.14" update-browserslist-db: "npm:^1.0.13" bin: browserslist: cli.js - checksum: 10/d46a906c79dfe95d9702c020afbe5b7b4dbe2019b85432e7a020326adff27e63e3c0a52dc8d4e73247060bbe2c13f000714741903cf96a16baae9c216dc74c75 + checksum: 10/496c3862df74565dd942b4ae65f502c575cbeba1fa4a3894dad7aa3b16130dc3033bc502d8848147f7b625154a284708253d9598bcdbef5a1e34cf11dc7bad8e languageName: node linkType: hard @@ -6328,14 +6342,16 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5": - version: 1.0.5 - resolution: "call-bind@npm:1.0.5" +"call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:^1.0.6, call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" function-bind: "npm:^1.1.2" - get-intrinsic: "npm:^1.2.1" - set-function-length: "npm:^1.1.1" - checksum: 10/246d44db6ef9bbd418828dbd5337f80b46be4398d522eded015f31554cbb2ea33025b0203b75c7ab05a1a255b56ef218880cca1743e4121e306729f9e414da39 + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: 10/cd6fe658e007af80985da5185bff7b55e12ef4c2b6f41829a26ed1eef254b1f1c12e3dfd5b2b068c6ba8b86aba62390842d81752e67dcbaec4f6f76e7113b6b7 languageName: node linkType: hard @@ -6377,10 +6393,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001580": - version: 1.0.30001580 - resolution: "caniuse-lite@npm:1.0.30001580" - checksum: 10/b626d25d792c766383a47c9efe1384f7d3c3b23e0ee70bf121e8b3a628806a96a21def16a44e58b75f4a5e23b5e008f51c2cc1e8be477b8c8d9493dcc170dd0e +"caniuse-lite@npm:^1.0.30001587": + version: 1.0.30001588 + resolution: "caniuse-lite@npm:1.0.30001588" + checksum: 10/09150ef2daa65c75cb2681832d5bc203760a02d9f71eb033dc0401fbfdbe026d3a84e54a8d2085f730a4f51eb074028b89013dd033841e1a0eb3c7323a50ed45 languageName: node linkType: hard @@ -6450,7 +6466,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.5.3, chokidar@npm:^3.4.3, chokidar@npm:^3.5.3": +"chokidar@npm:3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -6492,6 +6508,25 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^3.4.3, chokidar@npm:^3.5.3": + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: 10/c327fb07704443f8d15f7b4a7ce93b2f0bc0e6cea07ec28a7570aa22cd51fcf0379df589403976ea956c369f25aa82d84561947e227cd925902e1751371658df + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -6969,11 +7004,11 @@ __metadata: linkType: hard "core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.34.0": - version: 3.35.1 - resolution: "core-js-compat@npm:3.35.1" + version: 3.36.0 + resolution: "core-js-compat@npm:3.36.0" dependencies: - browserslist: "npm:^4.22.2" - checksum: 10/9a153c66591e23703e182b258ec6bdaff0a7c578dc5f9ac152fdfef2d09e8ec277f192e28d4634a8b576c8e1a6d3b1ac76ff6b8776e72b71b334e609e177a05e + browserslist: "npm:^4.22.3" + checksum: 10/633c49a254fe48981057e33651e5a74a0a14f14731aa5afed5d2e61fbe3c5cbc116ffd4feaa158c683c40d6dc4fd2e6aa0ebe12c45d157cfa571309d08400c98 languageName: node linkType: hard @@ -7216,14 +7251,14 @@ __metadata: languageName: node linkType: hard -"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1": - version: 1.1.1 - resolution: "define-data-property@npm:1.1.1" +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.2": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" dependencies: - get-intrinsic: "npm:^1.2.1" + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" gopd: "npm:^1.0.1" - has-property-descriptors: "npm:^1.0.0" - checksum: 10/5573c8df96b5857408cad64d9b91b69152e305ce4b06218e5f49b59c6cafdbb90a8bd8a0bb83c7bc67a8d479c04aa697063c9bc28d849b7282f9327586d6bc7b + checksum: 10/abdcb2505d80a53524ba871273e5da75e77e52af9e15b3aa65d8aad82b8a3a424dad7aee2cc0b71470ac7acf501e08defac362e8b6a73cdb4309f028061df4ae languageName: node linkType: hard @@ -7371,9 +7406,9 @@ __metadata: linkType: hard "diff@npm:^5.1.0": - version: 5.1.0 - resolution: "diff@npm:5.1.0" - checksum: 10/f4557032a98b2967fe27b1a91dfcf8ebb6b9a24b1afe616b5c2312465100b861e9b8d4da374be535f2d6b967ce2f53826d7f6edc2a0d32b2ab55abc96acc2f9d + version: 5.2.0 + resolution: "diff@npm:5.2.0" + checksum: 10/01b7b440f83a997350a988e9d2f558366c0f90f15be19f4aa7f1bb3109a4e153dfc3b9fbf78e14ea725717017407eeaa2271e3896374a0181e8f52445740846d languageName: node linkType: hard @@ -7495,10 +7530,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.648": - version: 1.4.648 - resolution: "electron-to-chromium@npm:1.4.648" - checksum: 10/a18f06bafce9017ac7b587f76dac77063a0beb7dfcdf9d5971f72b322f56af6315e4fc3c59154a260a9188c168ac7632538797d57a8c53ab57025ace0c9441f2 +"electron-to-chromium@npm:^1.4.668": + version: 1.4.673 + resolution: "electron-to-chromium@npm:1.4.673" + checksum: 10/e9e82bec564f4398040a3ce5a8e344dfebac624e458545c7d9317bb54d65f7000648e552acebc1951cda4562e5d9cebcb7d9e76a376c8ad1e04f7860230935fa languageName: node linkType: hard @@ -7598,11 +7633,11 @@ __metadata: linkType: hard "envinfo@npm:^7.7.3": - version: 7.11.0 - resolution: "envinfo@npm:7.11.0" + version: 7.11.1 + resolution: "envinfo@npm:7.11.1" bin: envinfo: dist/cli.js - checksum: 10/8cba09db181329b243fe02b3384ec275ebf93d5d3663c31e2064697aa96576c7de9b7e1c878a250f8eaec0db8026bace747709dcdc8d8a4ecd9a653cdbc08926 + checksum: 10/5a18ead05954ac1643350170fefce2436a9cb758dc402e36fe4616553ee46469f766fcb6df72379d1741a2e5b55918949b343ff6174502c31c524a5cf75f05cd languageName: node linkType: hard @@ -7631,50 +7666,75 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.22.1": - version: 1.22.3 - resolution: "es-abstract@npm:1.22.3" +"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3": + version: 1.22.4 + resolution: "es-abstract@npm:1.22.4" dependencies: - array-buffer-byte-length: "npm:^1.0.0" - arraybuffer.prototype.slice: "npm:^1.0.2" - available-typed-arrays: "npm:^1.0.5" - call-bind: "npm:^1.0.5" - es-set-tostringtag: "npm:^2.0.1" + array-buffer-byte-length: "npm:^1.0.1" + arraybuffer.prototype.slice: "npm:^1.0.3" + available-typed-arrays: "npm:^1.0.6" + call-bind: "npm:^1.0.7" + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + es-set-tostringtag: "npm:^2.0.2" es-to-primitive: "npm:^1.2.1" function.prototype.name: "npm:^1.1.6" - get-intrinsic: "npm:^1.2.2" - get-symbol-description: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + get-symbol-description: "npm:^1.0.2" globalthis: "npm:^1.0.3" gopd: "npm:^1.0.1" - has-property-descriptors: "npm:^1.0.0" + has-property-descriptors: "npm:^1.0.2" has-proto: "npm:^1.0.1" has-symbols: "npm:^1.0.3" - hasown: "npm:^2.0.0" - internal-slot: "npm:^1.0.5" - is-array-buffer: "npm:^3.0.2" + hasown: "npm:^2.0.1" + internal-slot: "npm:^1.0.7" + is-array-buffer: "npm:^3.0.4" is-callable: "npm:^1.2.7" is-negative-zero: "npm:^2.0.2" is-regex: "npm:^1.1.4" is-shared-array-buffer: "npm:^1.0.2" is-string: "npm:^1.0.7" - is-typed-array: "npm:^1.1.12" + is-typed-array: "npm:^1.1.13" is-weakref: "npm:^1.0.2" object-inspect: "npm:^1.13.1" object-keys: "npm:^1.1.1" - object.assign: "npm:^4.1.4" - regexp.prototype.flags: "npm:^1.5.1" - safe-array-concat: "npm:^1.0.1" - safe-regex-test: "npm:^1.0.0" + object.assign: "npm:^4.1.5" + regexp.prototype.flags: "npm:^1.5.2" + safe-array-concat: "npm:^1.1.0" + safe-regex-test: "npm:^1.0.3" string.prototype.trim: "npm:^1.2.8" string.prototype.trimend: "npm:^1.0.7" string.prototype.trimstart: "npm:^1.0.7" - typed-array-buffer: "npm:^1.0.0" + typed-array-buffer: "npm:^1.0.1" typed-array-byte-length: "npm:^1.0.0" typed-array-byte-offset: "npm:^1.0.0" typed-array-length: "npm:^1.0.4" unbox-primitive: "npm:^1.0.2" - which-typed-array: "npm:^1.1.13" - checksum: 10/e1ea9738ece15f810733b7bd71d825b555e01bb8c860272560d7d901467a9db1265214d6cf44f3beeb5d73ae421a609b9ad93a39aa47bbcd8cde510d5e0aa875 + which-typed-array: "npm:^1.1.14" + checksum: 10/062e562a000e280c0c0683ad4a7b81732f97463bc769110c668a8edb739cd5df56975fa55965f5304a3256fd6eee03b9b66a47d863076f8976c2050731946b1f + languageName: node + linkType: hard + +"es-array-method-boxes-properly@npm:^1.0.0": + version: 1.0.0 + resolution: "es-array-method-boxes-properly@npm:1.0.0" + checksum: 10/27a8a21acf20f3f51f69dce8e643f151e380bffe569e95dc933b9ded9fcd89a765ee21b5229c93f9206c93f87395c6b75f80be8ac8c08a7ceb8771e1822ff1fb + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 10/f66ece0a887b6dca71848fa71f70461357c0e4e7249696f81bad0a1f347eed7b31262af4a29f5d726dc026426f085483b6b90301855e647aa8e21936f07293c6 + languageName: node + linkType: hard + +"es-errors@npm:^1.0.0, es-errors@npm:^1.2.1, es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10/96e65d640156f91b707517e8cdc454dd7d47c32833aa3e85d79f24f9eb7ea85f39b63e36216ef0114996581969b59fe609a94e30316b08f5f4df1d44134cf8d5 languageName: node linkType: hard @@ -7685,7 +7745,7 @@ __metadata: languageName: node linkType: hard -"es-set-tostringtag@npm:^2.0.1": +"es-set-tostringtag@npm:^2.0.2": version: 2.0.2 resolution: "es-set-tostringtag@npm:2.0.2" dependencies: @@ -7696,7 +7756,7 @@ __metadata: languageName: node linkType: hard -"es-shim-unscopables@npm:^1.0.0": +"es-shim-unscopables@npm:^1.0.0, es-shim-unscopables@npm:^1.0.2": version: 1.0.2 resolution: "es-shim-unscopables@npm:1.0.2" dependencies: @@ -7761,9 +7821,9 @@ __metadata: linkType: hard "escalade@npm:^3.1.1": - version: 3.1.1 - resolution: "escalade@npm:3.1.1" - checksum: 10/afa618e73362576b63f6ca83c975456621095a1ed42ff068174e3f5cea48afc422814dda548c96e6ebb5333e7265140c7292abcc81bbd6ccb1757d50d3a4e182 + version: 3.1.2 + resolution: "escalade@npm:3.1.2" + checksum: 10/a1e07fea2f15663c30e40b9193d658397846ffe28ce0a3e4da0d8e485fedfeca228ab846aee101a05015829adf39f9934ff45b2a3fca47bed37a29646bd05cd3 languageName: node linkType: hard @@ -8417,11 +8477,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.16.0 - resolution: "fastq@npm:1.16.0" + version: 1.17.1 + resolution: "fastq@npm:1.17.1" dependencies: reusify: "npm:^1.0.4" - checksum: 10/de151543aab9d91900ed5da88860c46987ece925c628df586fac664235f25e020ec20729e1c032edb5fd2520fd4aa5b537d69e39b689e65e82112cfbecb4479e + checksum: 10/a443180068b527dd7b3a63dc7f2a47ceca2f3e97b9c00a1efe5538757e6cc4056a3526df94308075d7727561baf09ebaa5b67da8dcbddb913a021c5ae69d1f69 languageName: node linkType: hard @@ -8910,15 +8970,16 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2": - version: 1.2.2 - resolution: "get-intrinsic@npm:1.2.2" +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" dependencies: + es-errors: "npm:^1.3.0" function-bind: "npm:^1.1.2" has-proto: "npm:^1.0.1" has-symbols: "npm:^1.0.3" hasown: "npm:^2.0.0" - checksum: 10/aa96db4f809734d26d49b59bc8669d73a0ae792da561514e987735573a1dfaede516cd102f217a078ea2b42d4c4fb1f83d487932cb15d49826b726cc9cd4470b + checksum: 10/85bbf4b234c3940edf8a41f4ecbd4e25ce78e5e6ad4e24ca2f77037d983b9ef943fd72f00f3ee97a49ec622a506b67db49c36246150377efcda1c9eb03e5f06d languageName: node linkType: hard @@ -8943,13 +9004,14 @@ __metadata: languageName: node linkType: hard -"get-symbol-description@npm:^1.0.0": - version: 1.0.0 - resolution: "get-symbol-description@npm:1.0.0" +"get-symbol-description@npm:^1.0.2": + version: 1.0.2 + resolution: "get-symbol-description@npm:1.0.2" dependencies: - call-bind: "npm:^1.0.2" - get-intrinsic: "npm:^1.1.1" - checksum: 10/7e5f298afe0f0872747dce4a949ce490ebc5d6dd6aefbbe5044543711c9b19a4dfaebdbc627aee99e1299d58a435b2fbfa083458c1d58be6dc03a3bada24d359 + call-bind: "npm:^1.0.5" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + checksum: 10/e1cb53bc211f9dbe9691a4f97a46837a553c4e7caadd0488dc24ac694db8a390b93edd412b48dcdd0b4bbb4c595de1709effc75fc87c0839deedc6968f5bd973 languageName: node linkType: hard @@ -9343,12 +9405,12 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": - version: 1.0.1 - resolution: "has-property-descriptors@npm:1.0.1" +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1, has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" dependencies: - get-intrinsic: "npm:^1.2.2" - checksum: 10/21a47bb080a24e79594aef1ce71e1a18a1c5ab4120308e218088f67ebb7f6f408847541e2d96e5bd00e90eef5c5a49e4ebbdc8fc2d5b365a2c379aef071642f0 + es-define-property: "npm:^1.0.0" + checksum: 10/2d8c9ab8cebb572e3362f7d06139a4592105983d4317e68f7adba320fe6ddfc8874581e0971e899e633fd5f72e262830edce36d5a0bc863dad17ad20572484b2 languageName: node linkType: hard @@ -9366,12 +9428,12 @@ __metadata: languageName: node linkType: hard -"has-tostringtag@npm:^1.0.0": - version: 1.0.0 - resolution: "has-tostringtag@npm:1.0.0" +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.1": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" dependencies: - has-symbols: "npm:^1.0.2" - checksum: 10/95546e7132efc895a9ae64a8a7cf52588601fc3d52e0304ed228f336992cdf0baaba6f3519d2655e560467db35a1ed79f6420c286cc91a13aa0647a31ed92570 + has-symbols: "npm:^1.0.3" + checksum: 10/c74c5f5ceee3c8a5b8bc37719840dc3749f5b0306d818974141dda2471a1a2ca6c8e46b9d6ac222c5345df7a901c9b6f350b1e6d62763fec877e26609a401bfe languageName: node linkType: hard @@ -9414,12 +9476,12 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0": - version: 2.0.0 - resolution: "hasown@npm:2.0.0" +"hasown@npm:^2.0.0, hasown@npm:^2.0.1": + version: 2.0.1 + resolution: "hasown@npm:2.0.1" dependencies: function-bind: "npm:^1.1.2" - checksum: 10/c330f8d93f9d23fe632c719d4db3d698ef7d7c367d51548b836069e06a90fa9151e868c8e67353cfe98d67865bf7354855db28fa36eb1b18fa5d4a3f4e7f1c90 + checksum: 10/b7f9107387ee68abed88e965c2b99e868b5e0e9d289db1ddd080706ffafb69533b4f538b0e6362585bae8d6cbd080249f65e79702f74c225990f66d6106be3f6 languageName: node linkType: hard @@ -9812,12 +9874,12 @@ __metadata: linkType: hard "http-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "http-proxy-agent@npm:7.0.0" + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" dependencies: agent-base: "npm:^7.1.0" debug: "npm:^4.3.4" - checksum: 10/dbaaf3d9f3fc4df4a5d7ec45d456ec50f575240b557160fa63427b447d1f812dd7fe4a4f17d2e1ba003d231f07edf5a856ea6d91cb32d533062ff20a7803ccac + checksum: 10/d062acfa0cb82beeb558f1043c6ba770ea892b5fb7b28654dbc70ea2aeea55226dd34c02a294f6c1ca179a5aa483c4ea641846821b182edbd9cc5d89b54c6848 languageName: node linkType: hard @@ -9851,12 +9913,12 @@ __metadata: linkType: hard "https-proxy-agent@npm:^7.0.1": - version: 7.0.2 - resolution: "https-proxy-agent@npm:7.0.2" + version: 7.0.4 + resolution: "https-proxy-agent@npm:7.0.4" dependencies: agent-base: "npm:^7.0.2" debug: "npm:4" - checksum: 10/9ec844f78fd643608239c9c3f6819918631df5cd3e17d104cc507226a39b5d4adda9d790fc9fd63ac0d2bb8a761b2f9f60faa80584a9bf9d7f2e8c5ed0acd330 + checksum: 10/405fe582bba461bfe5c7e2f8d752b384036854488b828ae6df6a587c654299cbb2c50df38c4b6ab303502c3c5e029a793fbaac965d1e86ee0be03faceb554d63 languageName: node linkType: hard @@ -9923,9 +9985,9 @@ __metadata: linkType: hard "ignore@npm:^5.2.0, ignore@npm:^5.2.4": - version: 5.3.0 - resolution: "ignore@npm:5.3.0" - checksum: 10/51594355cea4c6ad6b28b3b85eb81afa7b988a1871feefd7062baf136c95aa06760ee934fa9590e43d967bd377ce84a4cf6135fbeb6063e063f1182a0e9a3bcd + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 10/0a884c2fbc8c316f0b9f92beaf84464253b73230a4d4d286697be45fca081199191ca33e1c2e82d9e5f851f5e9a48a78e25a35c951e7eb41e59f150db3530065 languageName: node linkType: hard @@ -10036,14 +10098,14 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.5": - version: 1.0.6 - resolution: "internal-slot@npm:1.0.6" +"internal-slot@npm:^1.0.5, internal-slot@npm:^1.0.7": + version: 1.0.7 + resolution: "internal-slot@npm:1.0.7" dependencies: - get-intrinsic: "npm:^1.2.2" + es-errors: "npm:^1.3.0" hasown: "npm:^2.0.0" side-channel: "npm:^1.0.4" - checksum: 10/bc2022eb1f277f2fcb2a60e7ced451c7ffc7a769b12e63c7a3fb247af8b5a1bed06428ce724046a8bca39ed6eb5b6832501a42f2e9a5ec4a9a7dc4e634431616 + checksum: 10/3e66720508831153ecf37d13def9f6856f9f2960989ec8a0a0476c98f887fca9eff0163127466485cb825c900c2d6fc601aa9117b7783b90ffce23a71ea5d053 languageName: node linkType: hard @@ -10080,6 +10142,16 @@ __metadata: languageName: node linkType: hard +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10/1ed81e06721af012306329b31f532b5e24e00cb537be18ddc905a84f19fe8f83a09a1699862bf3a1ec4b9dea93c55a3fa5faf8b5ea380431469df540f38b092c + languageName: node + linkType: hard + "ip@npm:^1.1.5": version: 1.1.8 resolution: "ip@npm:1.1.8" @@ -10087,13 +10159,6 @@ __metadata: languageName: node linkType: hard -"ip@npm:^2.0.0": - version: 2.0.0 - resolution: "ip@npm:2.0.0" - checksum: 10/1270b11e534a466fb4cf4426cbcc3a907c429389f7f4e4e3b288b42823562e88d6a509ceda8141a507de147ca506141f745005c0aa144569d94cf24a54eb52bc - languageName: node - linkType: hard - "ipaddr.js@npm:1.9.1": version: 1.9.1 resolution: "ipaddr.js@npm:1.9.1" @@ -10127,14 +10192,13 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": - version: 3.0.2 - resolution: "is-array-buffer@npm:3.0.2" +"is-array-buffer@npm:^3.0.4": + version: 3.0.4 + resolution: "is-array-buffer@npm:3.0.4" dependencies: call-bind: "npm:^1.0.2" - get-intrinsic: "npm:^1.2.0" - is-typed-array: "npm:^1.1.10" - checksum: 10/dcac9dda66ff17df9cabdc58214172bf41082f956eab30bb0d86bc0fab1e44b690fc8e1f855cf2481245caf4e8a5a006a982a71ddccec84032ed41f9d8da8c14 + get-intrinsic: "npm:^1.2.1" + checksum: 10/34a26213d981d58b30724ef37a1e0682f4040d580fa9ff58fdfdd3cefcb2287921718c63971c1c404951e7b747c50fdc7caf6e867e951353fa71b369c04c969b languageName: node linkType: hard @@ -10571,12 +10635,12 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.12, is-typed-array@npm:^1.1.9": - version: 1.1.12 - resolution: "is-typed-array@npm:1.1.12" +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.13, is-typed-array@npm:^1.1.9": + version: 1.1.13 + resolution: "is-typed-array@npm:1.1.13" dependencies: - which-typed-array: "npm:^1.1.11" - checksum: 10/d953adfd3c41618d5e01b2a10f21817e4cdc9572772fa17211100aebb3811b6e3c2e308a0558cc87d218a30504cb90154b833013437776551bfb70606fb088ca + which-typed-array: "npm:^1.1.14" + checksum: 10/f850ba08286358b9a11aee6d93d371a45e3c59b5953549ee1c1a9a55ba5c1dd1bd9952488ae194ad8f32a9cf5e79c8fa5f0cc4d78c00720aa0bbcf238b38062d languageName: node linkType: hard @@ -10668,9 +10732,9 @@ __metadata: linkType: hard "isbinaryfile@npm:^5.0.0": - version: 5.0.0 - resolution: "isbinaryfile@npm:5.0.0" - checksum: 10/511bb5ce54c903e9881ca46fe7fe06759b29d9d01d6929f3c46cb95a4bf369320d9de858943d5bf4ac446a442b53be095b325b1deebca0e67ccd9f466c53ef74 + version: 5.0.2 + resolution: "isbinaryfile@npm:5.0.2" + checksum: 10/515d7c963b35c2c443457d18c9152d1f655f3a0e2dceb548448e482145c1897e57a92fc024dece7de98c85c2909f5528e34e3d720c307887529cd689d7a7cd36 languageName: node linkType: hard @@ -10785,6 +10849,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10/bebe7ae829bbd586ce8cbe83501dd8cb8c282c8902a8aeeed0a073a89dc37e8103b1244f3c6acd60278bcbfe12d93a3f83c9ac396868a3b3bbc3c5e5e3b648ef + languageName: node + linkType: hard + "jsesc@npm:^2.5.1": version: 2.5.2 resolution: "jsesc@npm:2.5.2" @@ -11468,9 +11539,9 @@ __metadata: linkType: hard "lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.1.0 - resolution: "lru-cache@npm:10.1.0" - checksum: 10/207278d6fa711fb1f94a0835d4d4737441d2475302482a14785b10515e4c906a57ebf9f35bf060740c9560e91c7c1ad5a04fd7ed030972a9ba18bce2a228e95b + version: 10.2.0 + resolution: "lru-cache@npm:10.2.0" + checksum: 10/502ec42c3309c0eae1ce41afca471f831c278566d45a5273a0c51102dee31e0e250a62fa9029c3370988df33a14188a38e682c16143b794de78668de3643e302 languageName: node linkType: hard @@ -12066,15 +12137,15 @@ __metadata: linkType: hard "nise@npm:^5.1.5": - version: 5.1.7 - resolution: "nise@npm:5.1.7" + version: 5.1.9 + resolution: "nise@npm:5.1.9" dependencies: "@sinonjs/commons": "npm:^3.0.0" "@sinonjs/fake-timers": "npm:^11.2.2" "@sinonjs/text-encoding": "npm:^0.7.2" just-extend: "npm:^6.2.0" path-to-regexp: "npm:^6.2.1" - checksum: 10/4754e3ae52654f66e947d44d0dd40ae823e594c201474ad7c5115acb2188c839c9b8617504327051857aea8042befac946e82918e1e53b99350cb275140332d0 + checksum: 10/971caf7638d42a0e106eadd63f05adac1217f864b0a7e4519546aea82a0dbfac68586e7ff430704d54a01ff5dbf6cad58f5f67c067e21112a7deacd7789c2172 languageName: node linkType: hard @@ -12251,7 +12322,7 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.13.1": version: 1.13.1 resolution: "object-inspect@npm:1.13.1" checksum: 10/92f4989ed83422d56431bc39656d4c780348eb15d397ce352ade6b7fec08f973b53744bd41b94af021901e61acaf78fcc19e65bf464ecc0df958586a672700f0 @@ -12274,7 +12345,7 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:^4.0.4, object.assign@npm:^4.1.0, object.assign@npm:^4.1.2, object.assign@npm:^4.1.4": +"object.assign@npm:^4.0.4, object.assign@npm:^4.1.0, object.assign@npm:^4.1.2, object.assign@npm:^4.1.5": version: 4.1.5 resolution: "object.assign@npm:4.1.5" dependencies: @@ -12321,14 +12392,15 @@ __metadata: linkType: hard "object.groupby@npm:^1.0.1": - version: 1.0.1 - resolution: "object.groupby@npm:1.0.1" + version: 1.0.2 + resolution: "object.groupby@npm:1.0.2" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - es-abstract: "npm:^1.22.1" - get-intrinsic: "npm:^1.2.1" - checksum: 10/b7123d91403f95d63978513b23a6079c30f503311f64035fafc863c291c787f287b58df3b21ef002ce1d0b820958c9009dd5a8ab696e0eca325639d345e41524 + array.prototype.filter: "npm:^1.0.3" + call-bind: "npm:^1.0.5" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.22.3" + es-errors: "npm:^1.0.0" + checksum: 10/07c1bea1772c45f7967a63358a683ef7b0bd99cabe0563e6fee3e8acc061cc5984d2f01a46472ebf10b2cb439298c46776b2134550dce457fd7240baaaa4f592 languageName: node linkType: hard @@ -12621,12 +12693,12 @@ __metadata: linkType: hard "parse-bmfont-xml@npm:^1.1.4": - version: 1.1.4 - resolution: "parse-bmfont-xml@npm:1.1.4" + version: 1.1.6 + resolution: "parse-bmfont-xml@npm:1.1.6" dependencies: xml-parse-from-string: "npm:^1.0.0" - xml2js: "npm:^0.4.5" - checksum: 10/529d9c65da5e7840723d5382707d5a5177d25616e6ea434b4c474548e6229f1e64d0991bc9b38329762038e885c9097c562343007db78d9e9ca1e9b7157e6d7e + xml2js: "npm:^0.5.0" + checksum: 10/71a202da289a124db7bb7bee1b2a01b8a38b5ba36f93d6a98cea6fc1d140c16c8bc7bcccff48864ec886da035944d337b04cf70723393c411991af952fc6086b languageName: node linkType: hard @@ -13027,13 +13099,13 @@ __metadata: linkType: hard "postcss@npm:^8.4.14": - version: 8.4.33 - resolution: "postcss@npm:8.4.33" + version: 8.4.35 + resolution: "postcss@npm:8.4.35" dependencies: nanoid: "npm:^3.3.7" picocolors: "npm:^1.0.0" source-map-js: "npm:^1.0.2" - checksum: 10/e22a4594c255f26117f38419fb494d7ecab0f596cd409f7aadc8a6173abf180ed7ea970cd13fd366ab12b5840be901d2a09b25197700c2ebcb5a8077326bf519 + checksum: 10/93a7ce50cd6188f5f486a9ca98950ad27c19dfed996c45c414fa242944497e4d084a8760d3537f078630226f2bd3c6ab84b813b488740f4432e7c7039cd73a20 languageName: node linkType: hard @@ -13408,14 +13480,15 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.0, regexp.prototype.flags@npm:^1.5.1": - version: 1.5.1 - resolution: "regexp.prototype.flags@npm:1.5.1" +"regexp.prototype.flags@npm:^1.5.0, regexp.prototype.flags@npm:^1.5.2": + version: 1.5.2 + resolution: "regexp.prototype.flags@npm:1.5.2" dependencies: - call-bind: "npm:^1.0.2" - define-properties: "npm:^1.2.0" - set-function-name: "npm:^2.0.0" - checksum: 10/3fa5610b8e411bbc3a43ddfd13162f3a817beb43155fbd8caa24d4fd0ce2f431a8197541808772a5a06e5946cebfb68464c827827115bde0d11720a92fe2981a + call-bind: "npm:^1.0.6" + define-properties: "npm:^1.2.1" + es-errors: "npm:^1.3.0" + set-function-name: "npm:^2.0.1" + checksum: 10/9fffc01da9c4e12670ff95bc5204364615fcc12d86fc30642765af908675678ebb0780883c874b2dbd184505fb52fa603d80073ecf69f461ce7f56b15d10be9c languageName: node linkType: hard @@ -13703,9 +13776,9 @@ __metadata: linkType: hard "rfdc@npm:^1.3.0": - version: 1.3.0 - resolution: "rfdc@npm:1.3.0" - checksum: 10/76dedd9700cdf132947fde7ce1a8838c9cbb7f3e8f9188af0aaf97194cce745f42094dd2cf547426934cc83252ee2c0e432b2e0222a4415ab0db32de82665c69 + version: 1.3.1 + resolution: "rfdc@npm:1.3.1" + checksum: 10/44cc6a82e2fe1db13b7d3c54e9ffd0b40ef070cbde69ffbfbb38dab8cee46bd68ba686784b96365ff08d04798bc121c3465663a0c91f2c421c90546c4366f4a6 languageName: node linkType: hard @@ -13817,7 +13890,7 @@ __metadata: languageName: node linkType: hard -"safe-array-concat@npm:^1.0.1": +"safe-array-concat@npm:^1.1.0": version: 1.1.0 resolution: "safe-array-concat@npm:1.1.0" dependencies: @@ -13843,14 +13916,14 @@ __metadata: languageName: node linkType: hard -"safe-regex-test@npm:^1.0.0": - version: 1.0.2 - resolution: "safe-regex-test@npm:1.0.2" +"safe-regex-test@npm:^1.0.3": + version: 1.0.3 + resolution: "safe-regex-test@npm:1.0.3" dependencies: - call-bind: "npm:^1.0.5" - get-intrinsic: "npm:^1.2.2" + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" is-regex: "npm:^1.1.4" - checksum: 10/0e6a472caa8f44a502c7842ea19749de42c2eb1b41cb00456061dc3746cf3468e907522f56e97a15f3b41d88f660bd3d4f9bdec064a39895f7babae0f7aafc6a + checksum: 10/b04de61114b10274d92e25b6de7ccb5de07f11ea15637ff636de4b5190c0f5cd8823fe586dde718504cf78055437d70fd8804976894df502fcf5a210c970afb3 languageName: node linkType: hard @@ -13945,13 +14018,13 @@ __metadata: linkType: hard "semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.5.4": - version: 7.5.4 - resolution: "semver@npm:7.5.4" + version: 7.6.0 + resolution: "semver@npm:7.6.0" dependencies: lru-cache: "npm:^6.0.0" bin: semver: bin/semver.js - checksum: 10/985dec0d372370229a262c737063860fabd4a1c730662c1ea3200a2f649117761a42184c96df62a0e885e76fbd5dace41087d6c1ac0351b13c0df5d6bcb1b5ac + checksum: 10/1b41018df2d8aca5a1db4729985e8e20428c650daea60fcd16e926e9383217d00f574fab92d79612771884a98d2ee2a1973f49d630829a8d54d6570defe62535 languageName: node linkType: hard @@ -14053,20 +14126,21 @@ __metadata: languageName: node linkType: hard -"set-function-length@npm:^1.1.1": - version: 1.2.0 - resolution: "set-function-length@npm:1.2.0" +"set-function-length@npm:^1.2.1": + version: 1.2.1 + resolution: "set-function-length@npm:1.2.1" dependencies: - define-data-property: "npm:^1.1.1" + define-data-property: "npm:^1.1.2" + es-errors: "npm:^1.3.0" function-bind: "npm:^1.1.2" - get-intrinsic: "npm:^1.2.2" + get-intrinsic: "npm:^1.2.3" gopd: "npm:^1.0.1" has-property-descriptors: "npm:^1.0.1" - checksum: 10/6d609cd060c488d7d2178a5d4c3689f8a6afa26fa4c48ff4a0516664ff9b84c1c0898915777f5628092dab55c4fcead205525e2edd15c659423bf86f790fdcae + checksum: 10/9ab1d200149574ab27c1a7acae56d6235e02568fc68655fe8afe63e4e02ccad3c27665f55c32408bd1ff40705939dbb7539abfb9c3a07fda27ecad1ab9e449f5 languageName: node linkType: hard -"set-function-name@npm:^2.0.0": +"set-function-name@npm:^2.0.0, set-function-name@npm:^2.0.1": version: 2.0.1 resolution: "set-function-name@npm:2.0.1" dependencies: @@ -14143,13 +14217,14 @@ __metadata: linkType: hard "side-channel@npm:^1.0.4": - version: 1.0.4 - resolution: "side-channel@npm:1.0.4" + version: 1.0.5 + resolution: "side-channel@npm:1.0.5" dependencies: - call-bind: "npm:^1.0.0" - get-intrinsic: "npm:^1.0.2" - object-inspect: "npm:^1.9.0" - checksum: 10/c4998d9fc530b0e75a7fd791ad868fdc42846f072734f9080ff55cc8dc7d3899abcda24fd896aa6648c3ab7021b4bb478073eb4f44dfd55bce9714bc1a7c5d45 + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + object-inspect: "npm:^1.13.1" + checksum: 10/27708b70b5d81bf18dc8cc23f38f1b6c9511691a64abc4aaf17956e67d132c855cf8b46f931e2fc5a6262b29371eb60da7755c1b9f4f862eccea8562b469f8f6 languageName: node linkType: hard @@ -14281,12 +14356,12 @@ __metadata: linkType: hard "socks@npm:^2.7.1": - version: 2.7.1 - resolution: "socks@npm:2.7.1" + version: 2.8.0 + resolution: "socks@npm:2.8.0" dependencies: - ip: "npm:^2.0.0" + ip-address: "npm:^9.0.5" smart-buffer: "npm:^4.2.0" - checksum: 10/5074f7d6a13b3155fa655191df1c7e7a48ce3234b8ccf99afa2ccb56591c195e75e8bb78486f8e9ea8168e95a29573cbaad55b2b5e195160ae4d2ea6811ba833 + checksum: 10/ed0224ce2c7daaa7690cb87cf53d9703ffc4e983aca221f6f5b46767b232658df49494fd86acd0bf97ada6de05248ea8ea625c2343d48155d8463fc40d4a340f languageName: node linkType: hard @@ -14410,9 +14485,9 @@ __metadata: linkType: hard "spdx-exceptions@npm:^2.1.0": - version: 2.3.0 - resolution: "spdx-exceptions@npm:2.3.0" - checksum: 10/cb69a26fa3b46305637123cd37c85f75610e8c477b6476fa7354eb67c08128d159f1d36715f19be6f9daf4b680337deb8c65acdcae7f2608ba51931540687ac0 + version: 2.5.0 + resolution: "spdx-exceptions@npm:2.5.0" + checksum: 10/bb127d6e2532de65b912f7c99fc66097cdea7d64c10d3ec9b5e96524dbbd7d20e01cba818a6ddb2ae75e62bb0c63d5e277a7e555a85cbc8ab40044984fa4ae15 languageName: node linkType: hard @@ -14427,9 +14502,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.16 - resolution: "spdx-license-ids@npm:3.0.16" - checksum: 10/6425c54132ca38d717315cdbd2b620235937d1859972c5978bbc95b4c14400438ffe113709d8aabb0d5498cc27a5b89876fca0fe21b4e26f5ce122bc86d0d88e + version: 3.0.17 + resolution: "spdx-license-ids@npm:3.0.17" + checksum: 10/8f6c6ae02ebb25b4ca658b8990d9e8a8f8d8a95e1d8b9fd84d87eed80a7dc8f8073d6a8d50b8a0295c0e8399e1f8814f5c00e2985e6bf3731540a16f7241cbf1 languageName: node linkType: hard @@ -14469,6 +14544,13 @@ __metadata: languageName: node linkType: hard +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10/e7587128c423f7e43cc625fe2f87e6affdf5ca51c1cc468e910d8aaca46bb44a7fbcfa552f787b1d3987f7043aeb4527d1b99559e6621e01b42b3f45e5a24cbb + languageName: node + linkType: hard + "ssri@npm:^10.0.0": version: 10.0.5 resolution: "ssri@npm:10.0.5" @@ -14624,13 +14706,13 @@ __metadata: linkType: hard "string-width@npm:^7.0.0": - version: 7.0.0 - resolution: "string-width@npm:7.0.0" + version: 7.1.0 + resolution: "string-width@npm:7.1.0" dependencies: emoji-regex: "npm:^10.3.0" get-east-asian-width: "npm:^1.0.0" strip-ansi: "npm:^7.1.0" - checksum: 10/bc0de5700a2690895169fce447ec4ed44bc62de80312c2093d5606bfd48319bb88e48a99e97f269dff2bc9577448b91c26b3804c16e7d9b389699795e4655c3b + checksum: 10/a183573fe7209e0d294f661846d33f8caf72aa86d983e5b48a0ed45ab15bcccb02c6f0344b58b571988871105457137b8207855ea536827dbc4a376a0f31bf8f languageName: node linkType: hard @@ -14959,8 +15041,8 @@ __metadata: linkType: hard "terser@npm:^5.0.0, terser@npm:^5.15.1, terser@npm:^5.26.0": - version: 5.27.0 - resolution: "terser@npm:5.27.0" + version: 5.27.1 + resolution: "terser@npm:5.27.1" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.8.2" @@ -14968,7 +15050,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 10/9b2c5cb00747dea5994034ca064fb3cc7efc1be6b79a35247662d51ab43bdbe9cbf002bbf29170b5f3bd068c811d0212e22d94acd2cf0d8562687b96f1bffc9f + checksum: 10/4b5c8c65548071ae09dc1d9fd64616262876229897eaac9f95cf2e44908a1f4a25d7837c2a38caef1a523cf1cf67d254e74a846e9a854d289c0ad3664d581c3c languageName: node linkType: hard @@ -15174,11 +15256,11 @@ __metadata: linkType: hard "ts-api-utils@npm:^1.0.1": - version: 1.0.3 - resolution: "ts-api-utils@npm:1.0.3" + version: 1.2.1 + resolution: "ts-api-utils@npm:1.2.1" peerDependencies: typescript: ">=4.2.0" - checksum: 10/1350a5110eb1e534e9a6178f4081fb8a4fcc439749e19f4ad699baec9090fcb90fe532d5e191d91a062dc6e454a14a8d7eb2ad202f57135a30c4a44a3024f039 + checksum: 10/6d7f60fd01e3885bb334607f22b9cb1002e72da81dad2e672fef1b0d1a2f640b0f0ff5310369401488fac90c7a7f5d39c89fd18789af59c672c9b5aef4cade3e languageName: node linkType: hard @@ -15389,14 +15471,14 @@ __metadata: languageName: node linkType: hard -"typed-array-buffer@npm:^1.0.0": - version: 1.0.0 - resolution: "typed-array-buffer@npm:1.0.0" +"typed-array-buffer@npm:^1.0.1": + version: 1.0.1 + resolution: "typed-array-buffer@npm:1.0.1" dependencies: - call-bind: "npm:^1.0.2" - get-intrinsic: "npm:^1.2.1" - is-typed-array: "npm:^1.1.10" - checksum: 10/3e0281c79b2a40cd97fe715db803884301993f4e8c18e8d79d75fd18f796e8cd203310fec8c7fdb5e6c09bedf0af4f6ab8b75eb3d3a85da69328f28a80456bd3 + call-bind: "npm:^1.0.6" + es-errors: "npm:^1.3.0" + is-typed-array: "npm:^1.1.13" + checksum: 10/1d65e46b2b9b7ec2a30df39b9ddf32e55ad08d6119aec33975506a3dba56057796bdc3c64dbeb7fdb61bf340a75e279dfd55b48ce8f3b874f01731e1da6833d2 languageName: node linkType: hard @@ -15413,15 +15495,16 @@ __metadata: linkType: hard "typed-array-byte-offset@npm:^1.0.0": - version: 1.0.0 - resolution: "typed-array-byte-offset@npm:1.0.0" + version: 1.0.1 + resolution: "typed-array-byte-offset@npm:1.0.1" dependencies: - available-typed-arrays: "npm:^1.0.5" - call-bind: "npm:^1.0.2" + available-typed-arrays: "npm:^1.0.6" + call-bind: "npm:^1.0.7" for-each: "npm:^0.3.3" + gopd: "npm:^1.0.1" has-proto: "npm:^1.0.1" - is-typed-array: "npm:^1.1.10" - checksum: 10/2d81747faae31ca79f6c597dc18e15ae3d5b7e97f7aaebce3b31f46feeb2a6c1d6c92b9a634d901c83731ffb7ec0b74d05c6ff56076f5ae39db0cd19b16a3f92 + is-typed-array: "npm:^1.1.13" + checksum: 10/b174c0bac20bcd8787d2f5ccd7bd8f5e5a128e060ffe0909ffe27d65e486de50a3552248a307a45e5c9c593fd8ec97f5acdf119c3e13806f11943b7a2ce555be languageName: node linkType: hard @@ -16285,16 +16368,16 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.13": - version: 1.1.13 - resolution: "which-typed-array@npm:1.1.13" +"which-typed-array@npm:^1.1.14": + version: 1.1.14 + resolution: "which-typed-array@npm:1.1.14" dependencies: - available-typed-arrays: "npm:^1.0.5" - call-bind: "npm:^1.0.4" + available-typed-arrays: "npm:^1.0.6" + call-bind: "npm:^1.0.5" for-each: "npm:^0.3.3" gopd: "npm:^1.0.1" - has-tostringtag: "npm:^1.0.0" - checksum: 10/605e3e10b7118af904a0e79d0d50b95275102f06ec902734024989cd71354929f7acee50de43529d3baf5858e2e4eb32c75e6ebd226c888ad976d8140e4a3e71 + has-tostringtag: "npm:^1.0.1" + checksum: 10/56253d2c9d6b41b8a4af96d8c2751bac5508906bd500cdcd0dc5301fb082de0391a4311ab21258bc8d2609ed593f422c1a66f0020fcb3a1e97f719bc928b9018 languageName: node linkType: hard @@ -16659,13 +16742,13 @@ __metadata: languageName: node linkType: hard -"xml2js@npm:^0.4.5": - version: 0.4.23 - resolution: "xml2js@npm:0.4.23" +"xml2js@npm:^0.5.0": + version: 0.5.0 + resolution: "xml2js@npm:0.5.0" dependencies: sax: "npm:>=0.6.0" xmlbuilder: "npm:~11.0.0" - checksum: 10/52896ef39429f860f32471dd7bb2b89ef25b7e15528e3a4366de0bd5e55a251601565e7814763e70f9e75310c3afe649a42b8826442b74b41eff8a0ae333fccc + checksum: 10/27c4d759214e99be5ec87ee5cb1290add427fa43df509d3b92d10152b3806fd2f7c9609697a18b158ccf2caa01e96af067cdba93196f69ca10c90e4f79a08896 languageName: node linkType: hard From 2a803e09a4e200b23266abbc146a0270791a107e Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 20 Feb 2024 08:14:04 -0500 Subject: [PATCH 057/116] Detail view for energy devices graph (#19068) * Detail view for energy devices graph * Use getCommonOptions * Remove visibility toggle on horizontal bar chart * make a new card * unneeded translations * graph titles * Update src/translations/en.json Co-authored-by: Paul Bottein --------- Co-authored-by: Paul Bottein --- src/components/chart/ha-chart-base.ts | 21 +- .../energy/strategies/energy-view-strategy.ts | 7 + .../hui-energy-devices-detail-graph-card.ts | 412 ++++++++++++++++++ src/panels/lovelace/cards/types.ts | 7 + .../create-element/create-card-element.ts | 2 + src/translations/en.json | 3 +- 6 files changed, 447 insertions(+), 5 deletions(-) create mode 100644 src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index edccd2d22c..7bf2acc739 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -75,6 +75,8 @@ export class HaChartBase extends LitElement { private _paddingYAxisInternal = 0; + private _datasetOrder: number[] = []; + public disconnectedCallback() { super.disconnectedCallback(); this._releaseCanvas(); @@ -165,7 +167,17 @@ export class HaChartBase extends LitElement { } } + // put the legend labels in sorted order if provided if (changedProps.has("data")) { + this._datasetOrder = this.data.datasets.map((_, index) => index); + if (this.data?.datasets.some((dataset) => dataset.order)) { + this._datasetOrder.sort( + (a, b) => + (this.data.datasets[a].order || 0) - + (this.data.datasets[b].order || 0) + ); + } + if (this.externalHidden) { this._hiddenDatasets = new Set(); if (this.data?.datasets) { @@ -205,8 +217,9 @@ export class HaChartBase extends LitElement { ${this.options?.plugins?.legend?.display === true ? html`
    - ${this.data.datasets.map((dataset, index) => - this.extraData?.[index]?.show_legend === false + ${this._datasetOrder.map((index) => { + const dataset = this.data.datasets[index]; + return this.extraData?.[index]?.show_legend === false ? nothing : html`
  • -
  • ` - )} + `; + })}
` : ""} diff --git a/src/panels/energy/strategies/energy-view-strategy.ts b/src/panels/energy/strategies/energy-view-strategy.ts index 27a0a91371..d41ea668ba 100644 --- a/src/panels/energy/strategies/energy-view-strategy.ts +++ b/src/panels/energy/strategies/energy-view-strategy.ts @@ -154,6 +154,13 @@ export class EnergyViewStrategy extends ReactiveElement { // Only include if we have at least 1 device in the config. if (prefs.device_consumption.length) { + view.cards!.push({ + title: hass.localize( + "ui.panel.energy.cards.energy_devices_detail_graph_title" + ), + type: "energy-devices-detail-graph", + collection_key: "energy_dashboard", + }); view.cards!.push({ title: hass.localize( "ui.panel.energy.cards.energy_devices_graph_title" diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts new file mode 100644 index 0000000000..c13f2dac8e --- /dev/null +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts @@ -0,0 +1,412 @@ +import { + ChartData, + ChartDataset, + ChartOptions, + ScatterDataPoint, +} from "chart.js"; +import { differenceInDays, endOfToday, startOfToday } from "date-fns/esm"; +import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket"; +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + PropertyValues, +} from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import memoizeOne from "memoize-one"; +import { getColorByIndex } from "../../../../common/color/colors"; +import { ChartDatasetExtra } from "../../../../components/chart/ha-chart-base"; +import "../../../../components/ha-card"; +import { + DeviceConsumptionEnergyPreference, + EnergyData, + getEnergyDataCollection, +} from "../../../../data/energy"; +import { + calculateStatisticSumGrowth, + fetchStatistics, + getStatisticLabel, + Statistics, + StatisticsMetaData, + StatisticsUnitConfiguration, +} from "../../../../data/recorder"; +import { FrontendLocaleData } from "../../../../data/translation"; +import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceCard } from "../../types"; +import { EnergyDevicesDetailGraphCardConfig } from "../types"; +import { hasConfigChanged } from "../../common/has-changed"; +import { getCommonOptions } from "./common/energy-chart-options"; + +@customElement("hui-energy-devices-detail-graph-card") +export class HuiEnergyDevicesDetailGraphCard + extends SubscribeMixin(LitElement) + implements LovelaceCard +{ + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _config?: EnergyDevicesDetailGraphCardConfig; + + @state() private _chartData: ChartData = { datasets: [] }; + + @state() private _chartDatasetExtra: ChartDatasetExtra[] = []; + + @state() private _data?: EnergyData; + + @state() private _statistics?: Statistics; + + @state() private _compareStatistics?: Statistics; + + @state() private _start = startOfToday(); + + @state() private _end = endOfToday(); + + @state() private _compareStart?: Date; + + @state() private _compareEnd?: Date; + + @state() private _unit?: string; + + @state() private _hiddenStats = new Set(); + + protected hassSubscribeRequiredHostProps = ["_config"]; + + public hassSubscribe(): UnsubscribeFunc[] { + return [ + getEnergyDataCollection(this.hass, { + key: this._config?.collection_key, + }).subscribe(async (data) => { + this._data = data; + await this._getStatistics(this._data); + this._processStatistics(); + }), + ]; + } + + public getCardSize(): Promise | number { + return 3; + } + + public setConfig(config: EnergyDevicesDetailGraphCardConfig): void { + this._config = config; + } + + protected shouldUpdate(changedProps: PropertyValues): boolean { + return ( + hasConfigChanged(this, changedProps) || + changedProps.size > 1 || + !changedProps.has("hass") + ); + } + + protected willUpdate(changedProps: PropertyValues) { + if (changedProps.has("_hiddenStats") && this._statistics) { + this._processStatistics(); + } + } + + protected render() { + if (!this.hass || !this._config) { + return nothing; + } + + return html` + + ${this._config.title + ? html`

${this._config.title}

` + : ""} +
+ +
+
+ `; + } + + private _datasetHidden(ev) { + ev.stopPropagation(); + this._hiddenStats.add( + this._data!.prefs.device_consumption[ev.detail.index].stat_consumption + ); + this.requestUpdate("_hiddenStats"); + } + + private _datasetUnhidden(ev) { + ev.stopPropagation(); + this._hiddenStats.delete( + this._data!.prefs.device_consumption[ev.detail.index].stat_consumption + ); + this.requestUpdate("_hiddenStats"); + } + + private _createOptions = memoizeOne( + ( + start: Date, + end: Date, + locale: FrontendLocaleData, + config: HassConfig, + unit?: string, + compareStart?: Date, + compareEnd?: Date + ): ChartOptions => { + const commonOptions = getCommonOptions( + start, + end, + locale, + config, + unit, + compareStart, + compareEnd + ); + + const options: ChartOptions = { + ...commonOptions, + interaction: { + mode: "nearest", + }, + plugins: { + ...commonOptions.plugins!, + legend: { + display: true, + labels: { + usePointStyle: true, + }, + }, + }, + }; + return options; + } + ); + + private async _getStatistics(energyData: EnergyData): Promise { + const dayDifference = differenceInDays( + energyData.end || new Date(), + energyData.start + ); + + const devices = energyData.prefs.device_consumption.map( + (device) => device.stat_consumption + ); + + const period = + dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour"; + + const lengthUnit = this.hass.config.unit_system.length || ""; + const units: StatisticsUnitConfiguration = { + energy: "kWh", + volume: lengthUnit === "km" ? "mÂł" : "ftÂł", + }; + this._unit = "kWh"; + + const statistics = await fetchStatistics( + this.hass, + energyData.start, + energyData.end, + devices, + period, + units, + ["change"] + ); + + let compareStatistics: Statistics | undefined; + + if (energyData.startCompare && energyData.endCompare) { + compareStatistics = await fetchStatistics( + this.hass, + energyData.startCompare, + energyData.endCompare, + devices, + period, + units, + ["change"] + ); + } + this._statistics = statistics; + this._compareStatistics = compareStatistics; + } + + private async _processStatistics() { + const energyData = this._data!; + const data = this._statistics!; + const compareData = this._compareStatistics; + + const growthValues = {}; + energyData.prefs.device_consumption.forEach((device) => { + const value = + device.stat_consumption in data + ? calculateStatisticSumGrowth(data[device.stat_consumption]) || 0 + : 0; + + growthValues[device.stat_consumption] = value; + }); + + const sorted_devices = energyData.prefs.device_consumption.map( + (device) => device.stat_consumption + ); + sorted_devices.sort((a, b) => growthValues[b] - growthValues[a]); + + const datasets: ChartDataset<"bar", ScatterDataPoint[]>[] = []; + const datasetExtras: ChartDatasetExtra[] = []; + + datasets.push( + ...this._processDataSet( + data, + energyData.statsMetadata, + energyData.prefs.device_consumption, + sorted_devices + ) + ); + + const items = datasets.length; + datasetExtras.push(...Array(items).fill({})); + + if (compareData) { + // Add empty dataset to align the bars + datasets.push({ + order: 0, + data: [], + }); + datasetExtras.push({ + show_legend: false, + }); + datasets.push({ + order: 999, + data: [], + xAxisID: "xAxisCompare", + }); + datasetExtras.push({ + show_legend: false, + }); + + datasets.push( + ...this._processDataSet( + compareData, + energyData.statsMetadata, + energyData.prefs.device_consumption, + sorted_devices, + true + ) + ); + datasetExtras.push( + ...Array(items).fill({ show_legend: false }) + ); + } + + this._start = energyData.start; + this._end = energyData.end || endOfToday(); + + this._compareStart = energyData.startCompare; + this._compareEnd = energyData.endCompare; + + this._chartData = { + datasets, + }; + this._chartDatasetExtra = datasetExtras; + } + + private _processDataSet( + statistics: Statistics, + statisticsMetaData: Record, + devices: DeviceConsumptionEnergyPreference[], + sorted_devices: string[], + compare = false + ) { + const data: ChartDataset<"bar", ScatterDataPoint[]>[] = []; + + devices.forEach((source, idx) => { + const color = getColorByIndex(idx); + + let prevStart: number | null = null; + + const consumptionData: ScatterDataPoint[] = []; + + // Process gas consumption data. + if (source.stat_consumption in statistics) { + const stats = statistics[source.stat_consumption]; + let end; + + for (const point of stats) { + if (point.change === null || point.change === undefined) { + continue; + } + if (prevStart === point.start) { + continue; + } + const date = new Date(point.start); + consumptionData.push({ + x: date.getTime(), + y: point.change, + }); + prevStart = point.start; + end = point.end; + } + if (consumptionData.length === 1) { + consumptionData.push({ + x: end, + y: 0, + }); + } + } + + data.push({ + label: getStatisticLabel( + this.hass, + source.stat_consumption, + statisticsMetaData[source.stat_consumption] + ), + hidden: this._hiddenStats.has(source.stat_consumption), + borderColor: compare ? color + "7F" : color, + backgroundColor: compare ? color + "32" : color + "7F", + data: consumptionData, + order: 1 + sorted_devices.indexOf(source.stat_consumption), + stack: "devices", + pointStyle: compare ? false : "circle", + xAxisID: compare ? "xAxisCompare" : undefined, + }); + }); + return data; + } + + static get styles(): CSSResultGroup { + return css` + .card-header { + padding-bottom: 0; + } + .content { + padding: 16px; + } + .has-header { + padding-top: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-energy-devices-detail-graph-card": HuiEnergyDevicesDetailGraphCard; + } +} diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index d9a26be158..f87dde26cd 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -159,6 +159,13 @@ export interface EnergyDevicesGraphCardConfig extends LovelaceCardConfig { max_devices?: number; } +export interface EnergyDevicesDetailGraphCardConfig extends LovelaceCardConfig { + type: "energy-devices-detail-graph"; + title?: string; + collection_key?: string; + max_devices?: number; +} + export interface EnergySourcesTableCardConfig extends LovelaceCardConfig { type: "energy-sources-table"; title?: string; diff --git a/src/panels/lovelace/create-element/create-card-element.ts b/src/panels/lovelace/create-element/create-card-element.ts index 1884c79cc1..3619a13c8a 100644 --- a/src/panels/lovelace/create-element/create-card-element.ts +++ b/src/panels/lovelace/create-element/create-card-element.ts @@ -44,6 +44,8 @@ const LAZY_LOAD_TYPES = { import("../cards/energy/hui-energy-date-selection-card"), "energy-devices-graph": () => import("../cards/energy/hui-energy-devices-graph-card"), + "energy-devices-detail-graph": () => + import("../cards/energy/hui-energy-devices-detail-graph-card"), "energy-distribution": () => import("../cards/energy/hui-energy-distribution-card"), "energy-gas-graph": () => import("../cards/energy/hui-energy-gas-graph-card"), diff --git a/src/translations/en.json b/src/translations/en.json index d0cda35a16..1195ceb346 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6535,7 +6535,8 @@ "energy_water_graph_title": "Water consumption", "energy_distribution_title": "Energy distribution", "energy_sources_table_title": "Sources", - "energy_devices_graph_title": "Monitor individual devices" + "energy_devices_graph_title": "Individual devices total usage", + "energy_devices_detail_graph_title": "Individual devices detail usage" } }, "history": { From 86ec2725816f20da06c5c2cbff4dd1c600a1397e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 14:39:33 +0100 Subject: [PATCH 058/116] Update dependency @types/sortablejs to v1.15.8 (#19839) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 446ea474d9..56d12f7445 100644 --- a/package.json +++ b/package.json @@ -179,7 +179,7 @@ "@types/mocha": "10.0.6", "@types/qrcode": "1.5.5", "@types/serve-handler": "6.1.4", - "@types/sortablejs": "1.15.7", + "@types/sortablejs": "1.15.8", "@types/tar": "6.1.11", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", diff --git a/yarn.lock b/yarn.lock index 29f4b6d40e..c6a64c764d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4468,10 +4468,10 @@ __metadata: languageName: node linkType: hard -"@types/sortablejs@npm:1.15.7": - version: 1.15.7 - resolution: "@types/sortablejs@npm:1.15.7" - checksum: 10/422fb6a4862723567a0750339aff0ed8cfdbb4c5bc2601111481c1c2a23de5d6679c7f8dfd76daf2037e2d4df02e42fcd3203251aead64d5629968ead220cf10 +"@types/sortablejs@npm:1.15.8": + version: 1.15.8 + resolution: "@types/sortablejs@npm:1.15.8" + checksum: 10/aea58b08cf45f5e9633707a8df0df1212595c731bbdfd29805487138fdd0d8c51fa5c741999738a645c1e801d43a92ba0d3fb5b45625b52e247c56588aef6c55 languageName: node linkType: hard @@ -9599,7 +9599,7 @@ __metadata: "@types/mocha": "npm:10.0.6" "@types/qrcode": "npm:1.5.5" "@types/serve-handler": "npm:6.1.4" - "@types/sortablejs": "npm:1.15.7" + "@types/sortablejs": "npm:1.15.8" "@types/tar": "npm:6.1.11" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" From a26df88022c0771d688376a781093f7a2299638a Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:08:30 -0500 Subject: [PATCH 059/116] More accurate cardSize for Tile card (#19853) More accurate cardSize for tile card --- src/panels/lovelace/cards/hui-tile-card.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index b1c279091a..f85f182f88 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -117,7 +117,11 @@ export class HuiTileCard extends LitElement implements LovelaceCard { } public getCardSize(): number { - return 1; + return ( + 1 + + (this._config?.vertical ? 1 : 0) + + (this._config?.features?.length || 0) + ); } private _handleAction(ev: ActionHandlerEvent) { From af6dd545dc9d9128127980c5d0cffe8fa875b68b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 21:59:27 +0000 Subject: [PATCH 060/116] Update dependency @bundle-stats/plugin-webpack-filter to v4.10.1 (#19844) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 56d12f7445..57226a6d4e 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "@babel/plugin-transform-runtime": "7.23.9", "@babel/preset-env": "7.23.9", "@babel/preset-typescript": "7.23.3", - "@bundle-stats/plugin-webpack-filter": "4.10.0", + "@bundle-stats/plugin-webpack-filter": "4.10.1", "@koa/cors": "5.0.0", "@lokalise/node-api": "12.1.0", "@octokit/auth-oauth-device": "6.0.1", diff --git a/yarn.lock b/yarn.lock index c6a64c764d..863281863b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1447,12 +1447,12 @@ __metadata: languageName: node linkType: hard -"@bundle-stats/plugin-webpack-filter@npm:4.10.0": - version: 4.10.0 - resolution: "@bundle-stats/plugin-webpack-filter@npm:4.10.0" +"@bundle-stats/plugin-webpack-filter@npm:4.10.1": + version: 4.10.1 + resolution: "@bundle-stats/plugin-webpack-filter@npm:4.10.1" peerDependencies: core-js: ^3.0.0 - checksum: 10/43964bce792eab1e62571241278c71148b51513d262516823b7a8ba18c73da64e77cac2d93cd969db64f6699bad11bfb8f7d49de363bf4f9248b76e97479efcd + checksum: 10/f389188a27a01b8c353bb8b90a3c9fe1eabf4c24ed9f28c52e4063204780490dfd73df109d55ac0b0331a82b61705c0f3dac68b04fe50fcbd0e4e2efff2eda89 languageName: node linkType: hard @@ -9513,7 +9513,7 @@ __metadata: "@babel/preset-typescript": "npm:7.23.3" "@babel/runtime": "npm:7.23.9" "@braintree/sanitize-url": "npm:7.0.0" - "@bundle-stats/plugin-webpack-filter": "npm:4.10.0" + "@bundle-stats/plugin-webpack-filter": "npm:4.10.1" "@codemirror/autocomplete": "npm:6.12.0" "@codemirror/commands": "npm:6.3.3" "@codemirror/language": "npm:6.10.1" From a088b20987cadcefea0b8112775df1831909f260 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:43:38 -0500 Subject: [PATCH 061/116] Update dependency hls.js to v1.5.6 (#19843) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 57226a6d4e..ccb14c19d0 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,7 @@ "element-internals-polyfill": "1.3.10", "fuse.js": "7.0.0", "google-timezones-json": "1.2.0", - "hls.js": "1.5.5", + "hls.js": "1.5.6", "home-assistant-js-websocket": "9.1.0", "idb-keyval": "6.2.1", "intl-messageformat": "10.5.11", diff --git a/yarn.lock b/yarn.lock index 863281863b..d2802de708 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9494,10 +9494,10 @@ __metadata: languageName: node linkType: hard -"hls.js@npm:1.5.5": - version: 1.5.5 - resolution: "hls.js@npm:1.5.5" - checksum: 10/f182f9d1819527b76cf493646ce9a5cffaa9bb0fae02eab446fb16063fe15c527655a1237ba7f2ba3a1c51d11c64c25fb2cfe01ad94980038a810dc2ea982340 +"hls.js@npm:1.5.6": + version: 1.5.6 + resolution: "hls.js@npm:1.5.6" + checksum: 10/795c1d41b5ee682a48c21a78fa995ab8aa2b5badf84593eb12965b0fc58050ca5330df36a115946b8757dff558ba172f9210bb2896b12241416e069269e15d95 languageName: node linkType: hard @@ -9651,7 +9651,7 @@ __metadata: gulp-merge-json: "npm:2.1.2" gulp-rename: "npm:2.0.0" gulp-zopfli-green: "npm:6.0.1" - hls.js: "npm:1.5.5" + hls.js: "npm:1.5.6" home-assistant-js-websocket: "npm:9.1.0" html-minifier-terser: "npm:7.2.0" husky: "npm:9.0.11" From b360c854a8caff3907aabc7b1c5f2a24bd88dc43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:28:37 -0500 Subject: [PATCH 062/116] Bump ip from 1.1.8 to 1.1.9 (#19855) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d2802de708..a2e1e658ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10153,9 +10153,9 @@ __metadata: linkType: hard "ip@npm:^1.1.5": - version: 1.1.8 - resolution: "ip@npm:1.1.8" - checksum: 10/52975ebf84a090162d561fc6948fbc4c53775a8054c05371f09cfcb40e30a53aa225b4efb624f630cff5af2dd8124c82dd68e4df065dc1d1ca91d04e850e9cde + version: 1.1.9 + resolution: "ip@npm:1.1.9" + checksum: 10/29261559b806f64929ada21e6d7e3bf4e67f2b43a4cb67500fdb72cead2e655ce97451a2e325eca3f404081c634ff5c3a68472814744b7f2148ddffc0fdfe66c languageName: node linkType: hard From a6d73f76158b23d4bc102b6fd10eaaea0e0eddfb Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 22 Feb 2024 04:42:15 -0500 Subject: [PATCH 063/116] Allow unhiding entity hidden by integration (#19753) --- src/panels/config/entities/entity-registry-settings-editor.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/panels/config/entities/entity-registry-settings-editor.ts b/src/panels/config/entities/entity-registry-settings-editor.ts index 03102930af..5c6b8607fe 100644 --- a/src/panels/config/entities/entity-registry-settings-editor.ts +++ b/src/panels/config/entities/entity-registry-settings-editor.ts @@ -939,9 +939,7 @@ export class EntityRegistrySettingsEditor extends LitElement { > From 267fc3743d8456fe3ff633aef91733217ffa0e67 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 22 Feb 2024 10:42:37 +0100 Subject: [PATCH 064/116] Fix device class icon not showing in entities config page (#19854) --- src/data/icons.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/icons.ts b/src/data/icons.ts index dcb3952f55..f63a861e08 100644 --- a/src/data/icons.ts +++ b/src/data/icons.ts @@ -198,8 +198,9 @@ export const entryIcon = async ( if (entry.icon) { return entry.icon; } + const stateObj = hass.states[entry.entity_id] as HassEntity | undefined; const domain = computeDomain(entry.entity_id); - return getEntityIcon(hass, domain, undefined, undefined, entry); + return getEntityIcon(hass, domain, stateObj, undefined, entry); }; const getEntityIcon = async ( From 47f7cf5419f8fc3dacfe367e1dade2cf8cb5eb5b Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:23:06 -0500 Subject: [PATCH 065/116] Add devices to energy collection (#19849) * Include individual devices in energy collection * async cleanup, fix includeTypes filter --- src/data/energy.ts | 4 ++ .../hui-energy-devices-detail-graph-card.ts | 72 +++---------------- .../energy/hui-energy-devices-graph-card.ts | 47 +----------- 3 files changed, 15 insertions(+), 108 deletions(-) diff --git a/src/data/energy.ts b/src/data/energy.ts index fe7bf3d1a4..32340c0209 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -331,6 +331,9 @@ export const getReferencedStatisticIds = ( } } } + if (!(includeTypes && !includeTypes.includes("device"))) { + statIDs.push(...prefs.device_consumption.map((d) => d.stat_consumption)); + } return statIDs; }; @@ -383,6 +386,7 @@ const getEnergyData = async ( "solar", "battery", "gas", + "device", ]); const waterStatIds = getReferencedStatisticIds(prefs, info, ["water"]); diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts index c13f2dac8e..5bd0e8ce33 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-detail-graph-card.ts @@ -4,7 +4,7 @@ import { ChartOptions, ScatterDataPoint, } from "chart.js"; -import { differenceInDays, endOfToday, startOfToday } from "date-fns/esm"; +import { endOfToday, startOfToday } from "date-fns/esm"; import { HassConfig, UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, @@ -27,11 +27,9 @@ import { } from "../../../../data/energy"; import { calculateStatisticSumGrowth, - fetchStatistics, getStatisticLabel, Statistics, StatisticsMetaData, - StatisticsUnitConfiguration, } from "../../../../data/recorder"; import { FrontendLocaleData } from "../../../../data/translation"; import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; @@ -41,6 +39,8 @@ import { EnergyDevicesDetailGraphCardConfig } from "../types"; import { hasConfigChanged } from "../../common/has-changed"; import { getCommonOptions } from "./common/energy-chart-options"; +const UNIT = "kWh"; + @customElement("hui-energy-devices-detail-graph-card") export class HuiEnergyDevicesDetailGraphCard extends SubscribeMixin(LitElement) @@ -56,10 +56,6 @@ export class HuiEnergyDevicesDetailGraphCard @state() private _data?: EnergyData; - @state() private _statistics?: Statistics; - - @state() private _compareStatistics?: Statistics; - @state() private _start = startOfToday(); @state() private _end = endOfToday(); @@ -68,8 +64,6 @@ export class HuiEnergyDevicesDetailGraphCard @state() private _compareEnd?: Date; - @state() private _unit?: string; - @state() private _hiddenStats = new Set(); protected hassSubscribeRequiredHostProps = ["_config"]; @@ -78,9 +72,8 @@ export class HuiEnergyDevicesDetailGraphCard return [ getEnergyDataCollection(this.hass, { key: this._config?.collection_key, - }).subscribe(async (data) => { + }).subscribe((data) => { this._data = data; - await this._getStatistics(this._data); this._processStatistics(); }), ]; @@ -103,7 +96,7 @@ export class HuiEnergyDevicesDetailGraphCard } protected willUpdate(changedProps: PropertyValues) { - if (changedProps.has("_hiddenStats") && this._statistics) { + if (changedProps.has("_hiddenStats") && this._data) { this._processStatistics(); } } @@ -133,7 +126,7 @@ export class HuiEnergyDevicesDetailGraphCard this._end, this.hass.locale, this.hass.config, - this._unit, + UNIT, this._compareStart, this._compareEnd )} @@ -201,57 +194,10 @@ export class HuiEnergyDevicesDetailGraphCard } ); - private async _getStatistics(energyData: EnergyData): Promise { - const dayDifference = differenceInDays( - energyData.end || new Date(), - energyData.start - ); - - const devices = energyData.prefs.device_consumption.map( - (device) => device.stat_consumption - ); - - const period = - dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour"; - - const lengthUnit = this.hass.config.unit_system.length || ""; - const units: StatisticsUnitConfiguration = { - energy: "kWh", - volume: lengthUnit === "km" ? "mÂł" : "ftÂł", - }; - this._unit = "kWh"; - - const statistics = await fetchStatistics( - this.hass, - energyData.start, - energyData.end, - devices, - period, - units, - ["change"] - ); - - let compareStatistics: Statistics | undefined; - - if (energyData.startCompare && energyData.endCompare) { - compareStatistics = await fetchStatistics( - this.hass, - energyData.startCompare, - energyData.endCompare, - devices, - period, - units, - ["change"] - ); - } - this._statistics = statistics; - this._compareStatistics = compareStatistics; - } - - private async _processStatistics() { + private _processStatistics() { const energyData = this._data!; - const data = this._statistics!; - const compareData = this._compareStatistics; + const data = energyData.stats; + const compareData = energyData.statsCompare; const growthValues = {}; energyData.prefs.device_consumption.forEach((device) => { diff --git a/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts index 64a6b5eb84..19a69db447 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-devices-graph-card.ts @@ -6,7 +6,6 @@ import { ScatterDataPoint, } from "chart.js"; import { getRelativePosition } from "chart.js/helpers"; -import { differenceInDays } from "date-fns/esm"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, @@ -31,10 +30,7 @@ import "../../../../components/ha-card"; import { EnergyData, getEnergyDataCollection } from "../../../../data/energy"; import { calculateStatisticSumGrowth, - fetchStatistics, getStatisticLabel, - Statistics, - StatisticsUnitConfiguration, } from "../../../../data/recorder"; import { FrontendLocaleData } from "../../../../data/translation"; import { SubscribeMixin } from "../../../../mixins/subscribe-mixin"; @@ -186,47 +182,8 @@ export class HuiEnergyDevicesGraphCard ); private async _getStatistics(energyData: EnergyData): Promise { - const dayDifference = differenceInDays( - energyData.end || new Date(), - energyData.start - ); - - const devices = energyData.prefs.device_consumption.map( - (device) => device.stat_consumption - ); - - const period = - dayDifference > 35 ? "month" : dayDifference > 2 ? "day" : "hour"; - - const lengthUnit = this.hass.config.unit_system.length || ""; - const units: StatisticsUnitConfiguration = { - energy: "kWh", - volume: lengthUnit === "km" ? "mÂł" : "ftÂł", - }; - - const data = await fetchStatistics( - this.hass, - energyData.start, - energyData.end, - devices, - period, - units, - ["change"] - ); - - let compareData: Statistics | undefined; - - if (energyData.startCompare && energyData.endCompare) { - compareData = await fetchStatistics( - this.hass, - energyData.startCompare, - energyData.endCompare, - devices, - period, - units, - ["change"] - ); - } + const data = energyData.stats; + const compareData = energyData.statsCompare; const chartData: Array>["data"]> = []; From d95bf64edf85f226a47e00b2436d3b543cf4af82 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 22 Feb 2024 20:51:48 +0100 Subject: [PATCH 066/116] Add experimental sections view (#19846) --- src/common/util/array-move.ts | 8 +- src/components/ha-sortable.ts | 44 +- src/data/lovelace.ts | 13 + src/data/lovelace/config/section.ts | 26 ++ src/data/lovelace/config/view.ts | 2 + .../device-detail/ha-device-entities-card.ts | 8 +- src/panels/lovelace/cards/hui-button-card.ts | 10 + src/panels/lovelace/cards/hui-sensor-card.ts | 4 + src/panels/lovelace/cards/hui-tile-card.ts | 31 +- src/panels/lovelace/cards/types.ts | 2 +- .../common/generate-lovelace-config.ts | 22 +- .../lovelace/components/hui-card-edit-mode.ts | 303 +++++++++++++ .../lovelace/components/hui-card-options.ts | 69 +-- .../create-element/create-card-element.ts | 1 + .../create-element/create-element-base.ts | 17 +- .../create-element/create-section-element.ts | 19 + .../create-element/create-view-element.ts | 1 + .../lovelace/editor/add-entities-to-view.ts | 5 + .../editor/card-editor/hui-card-picker.ts | 176 ++++++-- .../card-editor/hui-dialog-create-card.ts | 65 ++- .../card-editor/hui-dialog-edit-card.ts | 65 +-- .../card-editor/hui-dialog-suggest-card.ts | 111 ++++- .../editor/card-editor/hui-section-preview.ts | 104 +++++ .../card-editor/show-create-card-dialog.ts | 4 +- .../card-editor/show-edit-card-dialog.ts | 16 +- .../card-editor/show-suggest-card-dialog.ts | 7 +- src/panels/lovelace/editor/config-util.ts | 403 ++++++++---------- src/panels/lovelace/editor/delete-card.ts | 19 +- src/panels/lovelace/editor/lovelace-path.ts | 197 +++++++++ src/panels/lovelace/editor/types.ts | 1 + .../unused-entities/hui-unused-entities.ts | 9 +- .../editor/view-editor/hui-view-editor.ts | 4 +- src/panels/lovelace/sections/const.ts | 2 + .../lovelace/sections/hui-error-section.ts | 60 +++ .../lovelace/sections/hui-grid-section.ts | 246 +++++++++++ src/panels/lovelace/sections/hui-section.ts | 247 +++++++++++ .../lovelace/strategies/get-strategy.ts | 46 +- src/panels/lovelace/strategies/types.ts | 6 +- src/panels/lovelace/types.ts | 1 + src/panels/lovelace/views/const.ts | 7 +- .../lovelace/views/hui-sections-view.ts | 322 ++++++++++++++ src/panels/lovelace/views/hui-view.ts | 112 ++++- src/translations/en.json | 27 +- .../lovelace/editor/config-util.spec.ts | 65 +-- 44 files changed, 2423 insertions(+), 484 deletions(-) create mode 100644 src/data/lovelace/config/section.ts create mode 100644 src/panels/lovelace/components/hui-card-edit-mode.ts create mode 100644 src/panels/lovelace/create-element/create-section-element.ts create mode 100644 src/panels/lovelace/editor/card-editor/hui-section-preview.ts create mode 100644 src/panels/lovelace/editor/lovelace-path.ts create mode 100644 src/panels/lovelace/sections/const.ts create mode 100644 src/panels/lovelace/sections/hui-error-section.ts create mode 100644 src/panels/lovelace/sections/hui-grid-section.ts create mode 100644 src/panels/lovelace/sections/hui-section.ts create mode 100644 src/panels/lovelace/views/hui-sections-view.ts diff --git a/src/common/util/array-move.ts b/src/common/util/array-move.ts index 36a152a019..0985f764c0 100644 --- a/src/common/util/array-move.ts +++ b/src/common/util/array-move.ts @@ -20,14 +20,14 @@ function findNestedItem( }, obj); } -export function nestedArrayMove( - obj: T | T[], +export function nestedArrayMove( + obj: A, oldIndex: number, newIndex: number, oldPath?: ItemPath, newPath?: ItemPath -): T | T[] { - const newObj = Array.isArray(obj) ? [...obj] : { ...obj }; +): A { + const newObj = (Array.isArray(obj) ? [...obj] : { ...obj }) as A; const from = oldPath ? findNestedItem(newObj, oldPath) : newObj; const to = newPath ? findNestedItem(newObj, newPath, true) : newObj; diff --git a/src/components/ha-sortable.ts b/src/components/ha-sortable.ts index 5258c2fc5f..1c83eef950 100644 --- a/src/components/ha-sortable.ts +++ b/src/components/ha-sortable.ts @@ -14,9 +14,16 @@ declare global { oldPath?: ItemPath; newPath?: ItemPath; }; + "drag-start": undefined; + "drag-end": undefined; } } +export type HaSortableOptions = Omit< + SortableInstance.SortableOptions, + "onStart" | "onChoose" | "onEnd" +>; + @customElement("ha-sortable") export class HaSortable extends LitElement { private _sortable?: SortableInstance; @@ -36,14 +43,17 @@ export class HaSortable extends LitElement { @property({ type: String, attribute: "handle-selector" }) public handleSelector?: string; - @property({ type: String, attribute: "group" }) - public group?: string; - - @property({ type: Number, attribute: "swap-threshold" }) - public swapThreshold?: number; + @property({ type: String }) + public group?: string | SortableInstance.GroupOptions; @property({ type: Boolean, attribute: "invert-swap" }) - public invertSwap?: boolean; + public invertSwap: boolean = false; + + @property({ attribute: false }) + public options?: HaSortableOptions; + + @property({ type: Boolean }) + public rollback: boolean = true; protected updated(changedProperties: PropertyValues) { if (changedProperties.has("disabled")) { @@ -114,26 +124,20 @@ export class HaSortable extends LitElement { const options: SortableInstance.Options = { animation: 150, - swapThreshold: 1, + ...this.options, onChoose: this._handleChoose, + onStart: this._handleStart, onEnd: this._handleEnd, }; if (this.draggableSelector) { options.draggable = this.draggableSelector; } - - if (this.swapThreshold !== undefined) { - options.swapThreshold = this.swapThreshold; - } - if (this.invertSwap !== undefined) { - options.invertSwap = this.invertSwap; - } if (this.handleSelector) { options.handle = this.handleSelector; } - if (this.draggableSelector) { - options.draggable = this.draggableSelector; + if (this.invertSwap !== undefined) { + options.invertSwap = this.invertSwap; } if (this.group) { options.group = this.group; @@ -143,8 +147,9 @@ export class HaSortable extends LitElement { } private _handleEnd = async (evt: SortableEvent) => { + fireEvent(this, "drag-end"); // put back in original location - if ((evt.item as any).placeholder) { + if (this.rollback && (evt.item as any).placeholder) { (evt.item as any).placeholder.replaceWith(evt.item); delete (evt.item as any).placeholder; } @@ -170,7 +175,12 @@ export class HaSortable extends LitElement { }); }; + private _handleStart = () => { + fireEvent(this, "drag-start"); + }; + private _handleChoose = (evt: SortableEvent) => { + if (!this.rollback) return; (evt.item as any).placeholder = document.createComment("sort-placeholder"); evt.item.after((evt.item as any).placeholder); }; diff --git a/src/data/lovelace.ts b/src/data/lovelace.ts index 1d6ba571ac..48de53e7d6 100644 --- a/src/data/lovelace.ts +++ b/src/data/lovelace.ts @@ -10,8 +10,10 @@ import { LovelaceCard, } from "../panels/lovelace/types"; import { HomeAssistant } from "../types"; +import { LovelaceSectionConfig } from "./lovelace/config/section"; import { fetchConfig, LegacyLovelaceConfig } from "./lovelace/config/types"; import { LovelaceViewConfig } from "./lovelace/config/view"; +import { HuiSection } from "../panels/lovelace/sections/hui-section"; export interface LovelacePanelConfig { mode: "yaml" | "storage"; @@ -24,10 +26,21 @@ export interface LovelaceViewElement extends HTMLElement { index?: number; cards?: Array; badges?: LovelaceBadge[]; + sections?: HuiSection[]; isStrategy: boolean; setConfig(config: LovelaceViewConfig): void; } +export interface LovelaceSectionElement extends HTMLElement { + hass?: HomeAssistant; + lovelace?: Lovelace; + viewIndex?: number; + index?: number; + cards?: Array; + isStrategy: boolean; + setConfig(config: LovelaceSectionConfig): void; +} + type LovelaceUpdatedEvent = HassEventBase & { event_type: "lovelace_updated"; data: { diff --git a/src/data/lovelace/config/section.ts b/src/data/lovelace/config/section.ts new file mode 100644 index 0000000000..1c21688585 --- /dev/null +++ b/src/data/lovelace/config/section.ts @@ -0,0 +1,26 @@ +import type { LovelaceCardConfig } from "./card"; +import type { LovelaceStrategyConfig } from "./strategy"; + +export interface LovelaceBaseSectionConfig { + title?: string; +} + +export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig { + type?: string; + cards?: LovelaceCardConfig[]; +} + +export interface LovelaceStrategySectionConfig + extends LovelaceBaseSectionConfig { + strategy: LovelaceStrategyConfig; +} + +export type LovelaceSectionRawConfig = + | LovelaceSectionConfig + | LovelaceStrategySectionConfig; + +export function isStrategySection( + section: LovelaceSectionRawConfig +): section is LovelaceStrategySectionConfig { + return "strategy" in section; +} diff --git a/src/data/lovelace/config/view.ts b/src/data/lovelace/config/view.ts index 0c522b60a1..10b454c15d 100644 --- a/src/data/lovelace/config/view.ts +++ b/src/data/lovelace/config/view.ts @@ -1,5 +1,6 @@ import type { LovelaceBadgeConfig } from "./badge"; import type { LovelaceCardConfig } from "./card"; +import type { LovelaceSectionRawConfig } from "./section"; import type { LovelaceStrategyConfig } from "./strategy"; export interface ShowViewConfig { @@ -23,6 +24,7 @@ export interface LovelaceViewConfig extends LovelaceBaseViewConfig { type?: string; badges?: Array; cards?: LovelaceCardConfig[]; + sections?: LovelaceSectionRawConfig[]; } export interface LovelaceStrategyViewConfig extends LovelaceBaseViewConfig { diff --git a/src/panels/config/devices/device-detail/ha-device-entities-card.ts b/src/panels/config/devices/device-detail/ha-device-entities-card.ts index d594df6952..71a8b0acdf 100644 --- a/src/panels/config/devices/device-detail/ha-device-entities-card.ts +++ b/src/panels/config/devices/device-detail/ha-device-entities-card.ts @@ -27,7 +27,10 @@ import { addEntitiesToLovelaceView } from "../../../lovelace/editor/add-entities import type { LovelaceRowConfig } from "../../../lovelace/entity-rows/types"; import { LovelaceRow } from "../../../lovelace/entity-rows/types"; import { EntityRegistryStateEntry } from "../ha-config-device-page"; -import { computeCards } from "../../../lovelace/common/generate-lovelace-config"; +import { + computeCards, + computeSection, +} from "../../../lovelace/common/generate-lovelace-config"; @customElement("ha-device-entities-card") export class HaDeviceEntitiesCard extends LitElement { @@ -235,6 +238,9 @@ export class HaDeviceEntitiesCard extends LitElement { computeCards(this.hass.states, entities, { title: this.deviceName, }), + computeSection(entities, { + title: this.deviceName, + }), entities ); } diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index d738a1be6c..129390fe5b 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -147,6 +147,16 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { ); } + public getGridSize(): [number, number] { + if ( + (this._config?.show_icon && this._config?.show_name) || + this._config?.show_state + ) { + return [2, 2]; + } + return [1, 1]; + } + public setConfig(config: ButtonCardConfig): void { if (config.entity && !isValidEntityId(config.entity)) { throw new Error("Invalid entity"); diff --git a/src/panels/lovelace/cards/hui-sensor-card.ts b/src/panels/lovelace/cards/hui-sensor-card.ts index ee7937e544..d90858862d 100644 --- a/src/panels/lovelace/cards/hui-sensor-card.ts +++ b/src/panels/lovelace/cards/hui-sensor-card.ts @@ -72,6 +72,10 @@ class HuiSensorCard extends HuiEntityCard { super.setConfig(entityCardConfig); } + public getSize(): [number, number] { + return [2, 2]; + } + static get styles(): CSSResultGroup { return [ HuiEntityCard.styles, diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index f85f182f88..1d56cc66bd 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -124,6 +124,18 @@ export class HuiTileCard extends LitElement implements LovelaceCard { ); } + public getGridSize(): [number, number] { + const width = 2; + let height = 1; + if (this._config?.features?.length) { + height += Math.ceil((this._config.features.length * 2) / 3); + } + if (this._config?.vertical) { + height++; + } + return [width, height]; + } + private _handleAction(ev: ActionHandlerEvent) { handleAction(this, this.hass!, this._config!, ev.detail.action!); } @@ -441,12 +453,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard { .secondary=${localizedState} > - + ${this._config.features + ? html` + + ` + : nothing} `; } @@ -469,6 +485,9 @@ export class HuiTileCard extends LitElement implements LovelaceCard { transition: box-shadow 180ms ease-in-out, border-color 180ms ease-in-out; + display: flex; + flex-direction: column; + justify-content: space-between; } ha-card.active { --tile-color: var(--state-icon-color); diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index f87dde26cd..8a57d1b0d5 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -545,7 +545,7 @@ export interface TileCardConfig extends LovelaceCardConfig { state_content?: string | string[]; icon?: string; color?: string; - show_entity_picture?: string; + show_entity_picture?: boolean; vertical?: boolean; tap_action?: ActionConfig; hold_action?: ActionConfig; diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 2b5437a93d..c609f66dbd 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -8,12 +8,14 @@ import { stripPrefixFromEntityName } from "../../../common/entity/strip_prefix_f import { stringCompare } from "../../../common/string/compare"; import { LocalizeFunc } from "../../../common/translations/localize"; import type { AreaFilterValue } from "../../../components/ha-area-filter"; +import { areaCompare } from "../../../data/area_registry"; import { EnergyPreferences, GridSourceTypeEnergyPreference, } from "../../../data/energy"; import { domainToName } from "../../../data/integration"; import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; import { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import { computeUserInitials } from "../../../data/user"; import { HomeAssistant } from "../../../types"; @@ -25,10 +27,10 @@ import { PictureCardConfig, PictureEntityCardConfig, ThermostatCardConfig, + TileCardConfig, } from "../cards/types"; import { EntityConfig } from "../entity-rows/types"; import { ButtonsHeaderFooterConfig } from "../header-footer/types"; -import { areaCompare } from "../../../data/area_registry"; const HIDE_DOMAIN = new Set([ "automation", @@ -100,6 +102,24 @@ const splitByAreaDevice = ( }; }; +export const computeSection = ( + entityIds: string[], + sectionOptions?: Partial +): LovelaceSectionConfig => ({ + type: "grid", + cards: entityIds.map( + (entity) => + ({ + type: "tile", + entity, + show_entity_picture: ["person", "camera", "image"].includes( + computeDomain(entity) + ), + }) as TileCardConfig + ), + ...sectionOptions, +}); + export const computeCards = ( states: HassEntities, entityIds: string[], diff --git a/src/panels/lovelace/components/hui-card-edit-mode.ts b/src/panels/lovelace/components/hui-card-edit-mode.ts new file mode 100644 index 0000000000..bae22490ba --- /dev/null +++ b/src/panels/lovelace/components/hui-card-edit-mode.ts @@ -0,0 +1,303 @@ +import "@material/mwc-button"; +import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; +import { + mdiContentCopy, + mdiContentCut, + mdiContentDuplicate, + mdiDelete, + mdiDotsVertical, + mdiPencil, +} from "@mdi/js"; +import deepClone from "deep-clone-simple"; +import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { storage } from "../../../common/decorators/storage"; +import { fireEvent } from "../../../common/dom/fire_event"; +import "../../../components/ha-button-menu"; +import "../../../components/ha-icon-button"; +import "../../../components/ha-list-item"; +import "../../../components/ha-svg-icon"; +import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; +import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; +import { + LovelaceCardPath, + findLovelaceCards, + getLovelaceContainerPath, + parseLovelaceCardPath, +} from "../editor/lovelace-path"; +import { Lovelace } from "../types"; + +@customElement("hui-card-edit-mode") +export class HuiCardEditMode extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public lovelace!: Lovelace; + + @property({ type: Array }) public path!: LovelaceCardPath; + + @property({ type: Boolean }) public hiddenOverlay = false; + + @state() + public _menuOpened: boolean = false; + + @state() + public _hover: boolean = false; + + @state() + public _focused: boolean = false; + + @storage({ + key: "lovelaceClipboard", + state: false, + subscribe: false, + storage: "sessionStorage", + }) + protected _clipboard?: LovelaceCardConfig; + + private get _cards() { + const containerPath = getLovelaceContainerPath(this.path!); + return findLovelaceCards(this.lovelace!.config, containerPath)!; + } + + private _touchStarted = false; + + protected firstUpdated(): void { + this.addEventListener("focus", () => { + this._focused = true; + }); + this.addEventListener("blur", () => { + this._focused = false; + }); + this.addEventListener("touchstart", () => { + this._touchStarted = true; + }); + this.addEventListener("touchend", () => { + setTimeout(() => { + this._touchStarted = false; + }, 10); + }); + this.addEventListener("mouseenter", () => { + if (this._touchStarted) return; + this._hover = true; + }); + this.addEventListener("mouseout", () => { + this._hover = false; + }); + this.addEventListener("click", () => { + this._hover = true; + document.addEventListener("click", this._documentClicked); + }); + } + + disconnectedCallback(): void { + super.disconnectedCallback(); + document.removeEventListener("click", this._documentClicked); + } + + _documentClicked = (ev) => { + this._hover = ev.composedPath().includes(this); + document.removeEventListener("click", this._documentClicked); + }; + + protected render(): TemplateResult { + const showOverlay = + (this._hover || this._menuOpened || this._focused) && !this.hiddenOverlay; + + return html` +
+
+
+
+ +
+ + + + + + ${this.hass.localize( + "ui.panel.lovelace.editor.edit_card.duplicate" + )} + + + + ${this.hass.localize("ui.panel.lovelace.editor.edit_card.copy")} + + + + ${this.hass.localize("ui.panel.lovelace.editor.edit_card.cut")} + +
  • + + ${this.hass.localize("ui.panel.lovelace.editor.edit_card.delete")} + + +
    +
    + `; + } + + private _handleOpened() { + this._menuOpened = true; + } + + private _handleClosed() { + this._menuOpened = false; + } + + private _handleAction(ev: CustomEvent) { + switch (ev.detail.index) { + case 0: + this._duplicateCard(); + break; + case 1: + this._copyCard(); + break; + case 2: + this._cutCard(); + break; + case 3: + this._deleteCard(true); + break; + } + } + + private _duplicateCard(): void { + const { cardIndex } = parseLovelaceCardPath(this.path!); + const containerPath = getLovelaceContainerPath(this.path!); + const cardConfig = this._cards![cardIndex]; + showEditCardDialog(this, { + lovelaceConfig: this.lovelace!.config, + saveConfig: this.lovelace!.saveConfig, + path: containerPath, + cardConfig, + }); + } + + private _editCard(ev): void { + if (ev.defaultPrevented) { + return; + } + if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") { + return; + } + ev.preventDefault(); + ev.stopPropagation(); + fireEvent(this, "ll-edit-card", { path: this.path! }); + } + + private _cutCard(): void { + this._copyCard(); + this._deleteCard(false); + } + + private _copyCard(): void { + const { cardIndex } = parseLovelaceCardPath(this.path!); + const cardConfig = this._cards[cardIndex]; + this._clipboard = deepClone(cardConfig); + } + + private _deleteCard(confirm: boolean): void { + fireEvent(this, "ll-delete-card", { path: this.path!, confirm }); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .card-overlay { + position: absolute; + opacity: 0; + pointer-events: none; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 180ms ease-in-out; + } + + .card-overlay.visible { + opacity: 1; + pointer-events: auto; + } + + .card-wrapper { + position: relative; + height: 100%; + z-index: 0; + } + + .edit { + outline: none !important; + cursor: pointer; + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--ha-card-border-radius, 12px); + z-index: 0; + } + .edit-overlay { + position: absolute; + inset: 0; + opacity: 0.8; + background-color: var(--primary-background-color); + border: 1px solid var(--divider-color); + border-radius: var(--ha-card-border-radius, 12px); + z-index: 0; + } + .edit ha-svg-icon { + display: flex; + position: relative; + color: var(--primary-text-color); + border-radius: 50%; + padding: 12px; + background: var(--secondary-background-color); + --mdc-icon-size: 24px; + } + .more { + position: absolute; + right: -6px; + top: -6px; + } + .more ha-icon-button { + cursor: pointer; + border-radius: 50%; + background: var(--secondary-background-color); + --mdc-icon-button-size: 32px; + --mdc-icon-size: 20px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-card-edit-mode": HuiCardEditMode; + } +} diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index 8d960e15f8..ec444bb954 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -28,7 +28,6 @@ import "../../../components/ha-icon-button"; import "../../../components/ha-list-item"; import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import { saveConfig } from "../../../data/lovelace/config/types"; -import { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import { showAlertDialog, showPromptDialog, @@ -41,10 +40,15 @@ import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog" import { addCard, deleteCard, - moveCard, - moveCardToPosition, - swapCard, + moveCardToContainer, + moveCardToIndex, } from "../editor/config-util"; +import { + LovelaceCardPath, + findLovelaceCards, + getLovelaceContainerPath, + parseLovelaceCardPath, +} from "../editor/lovelace-path"; import { showSelectViewDialog } from "../editor/select-view/show-select-view-dialog"; import { Lovelace, LovelaceCard } from "../types"; @@ -54,7 +58,7 @@ export class HuiCardOptions extends LitElement { @property({ attribute: false }) public lovelace?: Lovelace; - @property({ type: Array }) public path?: [number, number]; + @property({ type: Array }) public path?: LovelaceCardPath; @queryAssignedNodes() private _assignedNodes?: NodeListOf; @@ -76,17 +80,21 @@ export class HuiCardOptions extends LitElement { if (!changedProps.has("path") || !this.path) { return; } + const { viewIndex } = parseLovelaceCardPath(this.path); this.classList.toggle( "panel", - this.lovelace!.config.views[this.path![0]].panel + this.lovelace!.config.views[viewIndex].panel ); } - private get _currentView() { - return this.lovelace!.config.views[this.path![0]] as LovelaceViewConfig; + private get _cards() { + const containerPath = getLovelaceContainerPath(this.path!); + return findLovelaceCards(this.lovelace!.config, containerPath)!; } protected render(): TemplateResult { + const { cardIndex } = parseLovelaceCardPath(this.path!); + return html`
    @@ -107,7 +115,7 @@ export class HuiCardOptions extends LitElement { .path=${mdiMinus} class="move-arrow" @click=${this._decreaseCardPosiion} - ?disabled=${this.path![1] === 0} + ?disabled=${cardIndex === 0} > -
    ${this.path![1] + 1}
    +
    ${cardIndex + 1}
    ` : nothing} @@ -271,13 +278,14 @@ export class HuiCardOptions extends LitElement { } private _duplicateCard(): void { - const path = this.path!; - const cardConfig = this._currentView.cards![path[1]]; + const { cardIndex } = parseLovelaceCardPath(this.path!); + const containerPath = getLovelaceContainerPath(this.path!); + const cardConfig = this._cards![cardIndex]; showEditCardDialog(this, { lovelaceConfig: this.lovelace!.config, saveConfig: this.lovelace!.saveConfig, - path: [path[0], null], - newCardConfig: cardConfig, + path: containerPath, + cardConfig, }); } @@ -291,30 +299,29 @@ export class HuiCardOptions extends LitElement { } private _copyCard(): void { - const cardConfig = this._currentView.cards![this.path![1]]; + const { cardIndex } = parseLovelaceCardPath(this.path!); + const cardConfig = this._cards[cardIndex]; this._clipboard = deepClone(cardConfig); } private _decreaseCardPosiion(): void { const lovelace = this.lovelace!; const path = this.path!; - lovelace.saveConfig( - swapCard(lovelace.config, path, [path[0], path[1] - 1]) - ); + const { cardIndex } = parseLovelaceCardPath(path); + lovelace.saveConfig(moveCardToIndex(lovelace.config, path, cardIndex - 1)); } private _increaseCardPosition(): void { const lovelace = this.lovelace!; const path = this.path!; - lovelace.saveConfig( - swapCard(lovelace.config, path, [path[0], path[1] + 1]) - ); + const { cardIndex } = parseLovelaceCardPath(path); + lovelace.saveConfig(moveCardToIndex(lovelace.config, path, cardIndex + 1)); } private async _changeCardPosition(): Promise { const lovelace = this.lovelace!; const path = this.path!; - + const { cardIndex } = parseLovelaceCardPath(path); const positionString = await showPromptDialog(this, { title: this.hass!.localize( "ui.panel.lovelace.editor.change_position.title" @@ -324,7 +331,7 @@ export class HuiCardOptions extends LitElement { ), inputType: "number", inputMin: "1", - placeholder: String(path[1] + 1), + placeholder: String(cardIndex + 1), }); if (!positionString) return; @@ -333,7 +340,8 @@ export class HuiCardOptions extends LitElement { if (isNaN(position)) return; - lovelace.saveConfig(moveCardToPosition(lovelace.config, path, position)); + const newIndex = position - 1; + lovelace.saveConfig(moveCardToIndex(lovelace.config, path, newIndex)); } private _moveCard(): void { @@ -345,20 +353,17 @@ export class HuiCardOptions extends LitElement { viewSelectedCallback: async (urlPath, selectedDashConfig, viewIndex) => { if (urlPath === this.lovelace!.urlPath) { this.lovelace!.saveConfig( - moveCard(this.lovelace!.config, this.path!, [viewIndex]) + moveCardToContainer(this.lovelace!.config, this.path!, [viewIndex]) ); showSaveSuccessToast(this, this.hass!); return; } try { + const { cardIndex } = parseLovelaceCardPath(this.path!); await saveConfig( this.hass!, urlPath, - addCard( - selectedDashConfig, - [viewIndex], - this._currentView.cards![this.path![1]] - ) + addCard(selectedDashConfig, [viewIndex], this._cards[cardIndex]) ); this.lovelace!.saveConfig( deleteCard(this.lovelace!.config, this.path!) diff --git a/src/panels/lovelace/create-element/create-card-element.ts b/src/panels/lovelace/create-element/create-card-element.ts index 3619a13c8a..4a9a02fd67 100644 --- a/src/panels/lovelace/create-element/create-card-element.ts +++ b/src/panels/lovelace/create-element/create-card-element.ts @@ -24,6 +24,7 @@ const ALWAYS_LOADED_TYPES = new Set([ "entity-button", "glance", "grid", + "section", "light", "sensor", "thermostat", diff --git a/src/panels/lovelace/create-element/create-element-base.ts b/src/panels/lovelace/create-element/create-element-base.ts index d6d4c8d53f..48bf11bef9 100644 --- a/src/panels/lovelace/create-element/create-element-base.ts +++ b/src/panels/lovelace/create-element/create-element-base.ts @@ -1,27 +1,31 @@ import { fireEvent } from "../../../common/dom/fire_event"; -import { LovelaceViewElement } from "../../../data/lovelace"; +import { + LovelaceSectionElement, + LovelaceViewElement, +} from "../../../data/lovelace"; import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; import { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import { isCustomType, stripCustomPrefix, } from "../../../data/lovelace_custom_cards"; +import { LovelaceCardFeatureConfig } from "../card-features/types"; import type { HuiErrorCard } from "../cards/hui-error-card"; import type { ErrorCardConfig } from "../cards/types"; import { LovelaceElement, LovelaceElementConfig } from "../elements/types"; import { LovelaceRow, LovelaceRowConfig } from "../entity-rows/types"; import { LovelaceHeaderFooterConfig } from "../header-footer/types"; -import { LovelaceCardFeatureConfig } from "../card-features/types"; import { LovelaceBadge, LovelaceCard, LovelaceCardConstructor, + LovelaceCardFeature, + LovelaceCardFeatureConstructor, LovelaceHeaderFooter, LovelaceHeaderFooterConstructor, LovelaceRowConstructor, - LovelaceCardFeature, - LovelaceCardFeatureConstructor, } from "../types"; const TIMEOUT = 2000; @@ -62,6 +66,11 @@ interface CreateElementConfigTypes { element: LovelaceCardFeature; constructor: LovelaceCardFeatureConstructor; }; + section: { + config: LovelaceSectionConfig; + element: LovelaceSectionElement; + constructor: unknown; + }; } export const createErrorCardElement = (config: ErrorCardConfig) => { diff --git a/src/panels/lovelace/create-element/create-section-element.ts b/src/panels/lovelace/create-element/create-section-element.ts new file mode 100644 index 0000000000..7b63d2742e --- /dev/null +++ b/src/panels/lovelace/create-element/create-section-element.ts @@ -0,0 +1,19 @@ +import { LovelaceSectionElement } from "../../../data/lovelace"; +import { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; +import { HuiErrorCard } from "../cards/hui-error-card"; +import "../sections/hui-grid-section"; +import { createLovelaceElement } from "./create-element-base"; + +const ALWAYS_LOADED_LAYOUTS = new Set(["grid"]); + +const LAZY_LOAD_LAYOUTS = {}; + +export const createSectionElement = ( + config: LovelaceSectionConfig +): LovelaceSectionElement | HuiErrorCard => + createLovelaceElement( + "section", + config, + ALWAYS_LOADED_LAYOUTS, + LAZY_LOAD_LAYOUTS + ); diff --git a/src/panels/lovelace/create-element/create-view-element.ts b/src/panels/lovelace/create-element/create-view-element.ts index 158cf2da31..65bd3e30b2 100644 --- a/src/panels/lovelace/create-element/create-view-element.ts +++ b/src/panels/lovelace/create-element/create-view-element.ts @@ -9,6 +9,7 @@ const ALWAYS_LOADED_LAYOUTS = new Set(["masonry"]); const LAZY_LOAD_LAYOUTS = { panel: () => import("../views/hui-panel-view"), sidebar: () => import("../views/hui-sidebar-view"), + sections: () => import("../views/hui-sections-view"), }; export const createViewElement = ( diff --git a/src/panels/lovelace/editor/add-entities-to-view.ts b/src/panels/lovelace/editor/add-entities-to-view.ts index 03b9a44ac2..8706287c03 100644 --- a/src/panels/lovelace/editor/add-entities-to-view.ts +++ b/src/panels/lovelace/editor/add-entities-to-view.ts @@ -1,5 +1,6 @@ import { LovelacePanelConfig } from "../../../data/lovelace"; import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; import { LovelaceConfig, fetchConfig, @@ -15,6 +16,7 @@ export const addEntitiesToLovelaceView = async ( element: HTMLElement, hass: HomeAssistant, cardConfig: LovelaceCardConfig[], + sectionConfig?: LovelaceSectionConfig, entities?: string[] ) => { hass.loadFragmentTranslation("lovelace"); @@ -71,6 +73,7 @@ export const addEntitiesToLovelaceView = async ( // all storage dashboards are generated, but we have YAML dashboards just show the YAML config showSuggestCardDialog(element, { cardConfig, + sectionConfig, entities, yaml: true, }); @@ -93,6 +96,7 @@ export const addEntitiesToLovelaceView = async ( if (!storageDashs.length && lovelaceConfig.views.length === 1) { showSuggestCardDialog(element, { cardConfig, + sectionConfig, lovelaceConfig: lovelaceConfig!, saveConfig: async (newConfig: LovelaceConfig): Promise => { try { @@ -116,6 +120,7 @@ export const addEntitiesToLovelaceView = async ( viewSelectedCallback: (newUrlPath, selectedDashConfig, viewIndex) => { showSuggestCardDialog(element, { cardConfig, + sectionConfig, lovelaceConfig: selectedDashConfig, saveConfig: async (newConfig: LovelaceConfig): Promise => { try { diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts index 382914e87f..2577822756 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -15,6 +15,7 @@ import { until } from "lit/directives/until"; import memoizeOne from "memoize-one"; import { storage } from "../../../../common/decorators/storage"; import { fireEvent } from "../../../../common/dom/fire_event"; +import { stringCompare } from "../../../../common/string/compare"; import "../../../../components/ha-circular-progress"; import "../../../../components/search-input"; import { isUnavailableState } from "../../../../data/entity"; @@ -46,6 +47,8 @@ interface CardElement { export class HuiCardPicker extends LitElement { @property({ attribute: false }) public hass?: HomeAssistant; + @property({ attribute: false }) public suggestedCards?: string[]; + @storage({ key: "lovelaceClipboard", state: true, @@ -92,6 +95,29 @@ export class HuiCardPicker extends LitElement { } ); + private _suggestedCards = memoizeOne( + (cardElements: CardElement[]): CardElement[] => + cardElements.filter( + (cardElement: CardElement) => cardElement.card.isSuggested + ) + ); + + private _customCards = memoizeOne( + (cardElements: CardElement[]): CardElement[] => + cardElements.filter( + (cardElement: CardElement) => + cardElement.card.isCustom && !cardElement.card.isSuggested + ) + ); + + private _otherCards = memoizeOne( + (cardElements: CardElement[]): CardElement[] => + cardElements.filter( + (cardElement: CardElement) => + !cardElement.card.isSuggested && !cardElement.card.isCustom + ) + ); + protected render() { if ( !this.hass || @@ -102,6 +128,10 @@ export class HuiCardPicker extends LitElement { return nothing; } + const suggestedCards = this._suggestedCards(this._cards); + const othersCards = this._otherCards(this._cards); + const customCardsItems = this._customCards(this._cards); + return html`
    - ${this._clipboard && !this._filter - ? html` - ${until( - this._renderCardElement( - { - type: this._clipboard.type, - showElement: true, - isCustom: false, - name: this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.paste" - ), - description: `${this.hass!.localize( - "ui.panel.lovelace.editor.card.generic.paste_description", - { - type: this._clipboard.type, - } - )}`, - }, - this._clipboard - ), - html` -
    - -
    - ` + ${this._filter + ? this._filterCards(this._cards, this._filter).map( + (cardElement: CardElement) => cardElement.element + ) + : html` + ${suggestedCards.length > 0 + ? html` +
    + ${this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.suggested_cards` + )} +
    + ` + : nothing} + ${this._renderClipboardCard()} + ${suggestedCards.map( + (cardElement: CardElement) => cardElement.element )} - ` - : nothing} - ${this._filterCards(this._cards, this._filter).map( - (cardElement: CardElement) => cardElement.element - )} + ${suggestedCards.length > 0 + ? html` +
    + ${this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.other_cards` + )} +
    + ` + : nothing} + ${othersCards.map( + (cardElement: CardElement) => cardElement.element + )} + ${customCardsItems.length > 0 + ? html` +
    + ${this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.custom_cards` + )} +
    + ` + : nothing} + ${customCardsItems.map( + (cardElement: CardElement) => cardElement.element + )} + `}
    { + if (a.isSuggested && !b.isSuggested) { + return -1; + } + if (!a.isSuggested && b.isSuggested) { + return 1; + } + return stringCompare( + a.name || a.type, + b.name || b.type, + this.hass?.language + ); + }); + if (customCards.length > 0) { cards = cards.concat( customCards.map((ccard: CustomCardEntry) => ({ @@ -244,6 +300,37 @@ export class HuiCardPicker extends LitElement { })); } + private _renderClipboardCard() { + if (!this._clipboard) { + return nothing; + } + + return html` ${until( + this._renderCardElement( + { + type: this._clipboard.type, + showElement: true, + isCustom: false, + name: this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.paste" + ), + description: `${this.hass!.localize( + "ui.panel.lovelace.editor.card.generic.paste_description", + { + type: this._clipboard.type, + } + )}`, + }, + this._clipboard + ), + html` +
    + +
    + ` + )}`; + } + private _handleSearchChange(ev: CustomEvent) { const value = ev.detail.value; @@ -381,6 +468,14 @@ export class HuiCardPicker extends LitElement { margin: var(--card-picker-search-margin); } + .cards-container-header { + font-size: 16px; + font-weight: 500; + padding: 12px 8px 4px 8px; + margin: 0; + grid-column: 1 / -1; + } + .cards-container { display: grid; grid-gap: 8px 8px; @@ -455,6 +550,23 @@ export class HuiCardPicker extends LitElement { .manual { max-width: none; } + + .icon { + position: absolute; + top: 8px; + right: 8px + inset-inline-start: 8px; + inset-inline-end: 8px; + border-radius: 50%; + --mdc-icon-size: 16px; + line-height: 16px; + box-sizing: border-box; + color: var(--text-primary-color); + padding: 4px; + } + .icon.custom { + background: var(--warning-color); + } `, ]; } diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts index 3c7798411f..89a20ab0f5 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -12,16 +12,27 @@ import { computeStateName } from "../../../../common/entity/compute_state_name"; import { DataTableRowData } from "../../../../components/data-table/ha-data-table"; import "../../../../components/ha-dialog"; import "../../../../components/ha-dialog-header"; +import { + isStrategySection, + LovelaceSectionConfig, +} from "../../../../data/lovelace/config/section"; import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; import { haStyleDialog } from "../../../../resources/styles"; import type { HomeAssistant } from "../../../../types"; +import { + computeCards, + computeSection, +} from "../../common/generate-lovelace-config"; import "./hui-card-picker"; import "./hui-entity-picker-table"; import { CreateCardDialogParams } from "./show-create-card-dialog"; import { showEditCardDialog } from "./show-edit-card-dialog"; import { showSuggestCardDialog } from "./show-suggest-card-dialog"; -import { computeCards } from "../../common/generate-lovelace-config"; +import { + findLovelaceContainer, + parseLovelaceContainerPath, +} from "../lovelace-path"; declare global { interface HASSDomEvents { @@ -42,7 +53,9 @@ export class HuiCreateDialogCard @state() private _params?: CreateCardDialogParams; - @state() private _viewConfig!: LovelaceViewConfig; + @state() private _containerConfig!: + | LovelaceViewConfig + | LovelaceSectionConfig; @state() private _selectedEntities: string[] = []; @@ -50,8 +63,17 @@ export class HuiCreateDialogCard public async showDialog(params: CreateCardDialogParams): Promise { this._params = params; - const [view] = params.path; - this._viewConfig = params.lovelaceConfig.views[view]; + + const containerConfig = findLovelaceContainer( + params.lovelaceConfig, + params.path + ); + + if ("strategy" in containerConfig) { + throw new Error("Can't edit strategy"); + } + + this._containerConfig = containerConfig; } public closeDialog(): boolean { @@ -67,10 +89,10 @@ export class HuiCreateDialogCard return nothing; } - const title = this._viewConfig.title + const title = this._containerConfig.title ? this.hass!.localize( - "ui.panel.lovelace.editor.edit_card.pick_card_view_title", - { name: `"${this._viewConfig.title}"` } + "ui.panel.lovelace.editor.edit_card.pick_card_title", + { name: `"${this._containerConfig.title}"` } ) : this.hass!.localize("ui.panel.lovelace.editor.edit_card.pick_card"); @@ -112,6 +134,7 @@ export class HuiCreateDialogCard this._currTabIndex === 0 ? html` = {}; + + const { sectionIndex } = parseLovelaceContainerPath(this._params!.path); + const isSection = sectionIndex !== undefined; + + // If we are in a section, we want to keep the section options for the preview + if (isSection) { + const containerConfig = findLovelaceContainer( + this._params!.lovelaceConfig!, + this._params!.path! + ) as LovelaceSectionConfig; + if (!isStrategySection(containerConfig)) { + const { cards, title, ...rest } = containerConfig; + sectionOptions = rest; + } + } + + const sectionConfig = computeSection( + this._selectedEntities, + sectionOptions + ); + showSuggestCardDialog(this, { lovelaceConfig: this._params!.lovelaceConfig, saveConfig: this._params!.saveConfig, path: this._params!.path as [number], entities: this._selectedEntities, cardConfig, + sectionConfig, }); this.closeDialog(); diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index 44e4703567..c27b7024a2 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -1,12 +1,12 @@ import { mdiClose, mdiHelpCircle } from "@mdi/js"; import deepFreeze from "deep-freeze"; import { - css, CSSResultGroup, - html, LitElement, - nothing, PropertyValues, + css, + html, + nothing, } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import type { HASSDomEvent } from "../../../../common/dom/fire_event"; @@ -16,6 +16,14 @@ import "../../../../components/ha-circular-progress"; import "../../../../components/ha-dialog"; import "../../../../components/ha-dialog-header"; import "../../../../components/ha-icon-button"; +import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; +import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; +import { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; +import { + getCustomCardEntry, + isCustomType, + stripCustomPrefix, +} from "../../../../data/lovelace_custom_cards"; import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; import { haStyleDialog } from "../../../../resources/styles"; @@ -24,18 +32,12 @@ import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; import { addCard, replaceCard } from "../config-util"; import { getCardDocumentationURL } from "../get-card-documentation-url"; import type { ConfigChangedEvent } from "../hui-element-editor"; +import { findLovelaceContainer } from "../lovelace-path"; import type { GUIModeChangedEvent } from "../types"; import "./hui-card-element-editor"; import type { HuiCardElementEditor } from "./hui-card-element-editor"; import "./hui-card-preview"; import type { EditCardDialogParams } from "./show-edit-card-dialog"; -import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; -import { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; -import { - getCustomCardEntry, - isCustomType, - stripCustomPrefix, -} from "../../../../data/lovelace_custom_cards"; declare global { // for fire event @@ -61,7 +63,9 @@ export class HuiDialogEditCard @state() private _cardConfig?: LovelaceCardConfig; - @state() private _viewConfig!: LovelaceViewConfig; + @state() private _containerConfig!: + | LovelaceViewConfig + | LovelaceSectionConfig; @state() private _saving = false; @@ -84,18 +88,29 @@ export class HuiDialogEditCard this._params = params; this._GUImode = true; this._guiModeAvailable = true; - const [view, card] = params.path; - this._viewConfig = params.lovelaceConfig.views[view]; - this._cardConfig = - params.newCardConfig ?? - (card !== null ? this._viewConfig.cards![card] : undefined); + + const containerConfig = findLovelaceContainer( + params.lovelaceConfig, + params.path + ); + + if ("strategy" in containerConfig) { + throw new Error("Can't edit strategy"); + } + + this._containerConfig = containerConfig; + + if ("cardConfig" in params) { + this._cardConfig = params.cardConfig; + this._dirty = true; + } else { + this._cardConfig = this._containerConfig.cards?.[params.cardIndex]; + } + this.large = false; if (this._cardConfig && !Object.isFrozen(this._cardConfig)) { this._cardConfig = deepFreeze(this._cardConfig); } - if (params.newCardConfig) { - this._dirty = true; - } } public closeDialog(): boolean { @@ -171,10 +186,10 @@ export class HuiDialogEditCard { type: cardName } ); } else if (!this._cardConfig) { - heading = this._viewConfig.title + heading = this._containerConfig.title ? this.hass!.localize( "ui.panel.lovelace.editor.edit_card.pick_card_view_title", - { name: this._viewConfig.title } + { name: this._containerConfig.title } ) : this.hass!.localize("ui.panel.lovelace.editor.edit_card.pick_card"); } else { @@ -369,13 +384,13 @@ export class HuiDialogEditCard return; } this._saving = true; - const [view, card] = this._params!.path; + const path = this._params!.path; await this._params!.saveConfig( - card === null - ? addCard(this._params!.lovelaceConfig, [view], this._cardConfig!) + "cardConfig" in this._params! + ? addCard(this._params!.lovelaceConfig, path, this._cardConfig!) : replaceCard( this._params!.lovelaceConfig, - [view, card], + [...path, this._params!.cardIndex], this._cardConfig! ) ); diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts index 8cf248ac01..80e517d5ce 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts @@ -5,13 +5,20 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import "../../../../components/ha-yaml-editor"; import type { HaYamlEditor } from "../../../../components/ha-yaml-editor"; import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; +import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; +import { isStrategyView } from "../../../../data/lovelace/config/view"; import { haStyleDialog } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; -import { addCards } from "../config-util"; +import { addCards, addSection } from "../config-util"; +import { + LovelaceContainerPath, + parseLovelaceContainerPath, +} from "../lovelace-path"; import "./hui-card-preview"; import { showCreateCardDialog } from "./show-create-card-dialog"; import { SuggestCardDialogParams } from "./show-suggest-card-dialog"; +import { LovelaceConfig } from "../../../../data/lovelace/config/types"; @customElement("hui-dialog-suggest-card") export class HuiDialogSuggestCard extends LitElement { @@ -21,6 +28,8 @@ export class HuiDialogSuggestCard extends LitElement { @state() private _cardConfig?: LovelaceCardConfig[]; + @state() private _sectionConfig?: LovelaceSectionConfig; + @state() private _saving = false; @query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor; @@ -28,9 +37,13 @@ export class HuiDialogSuggestCard extends LitElement { public showDialog(params: SuggestCardDialogParams): void { this._params = params; this._cardConfig = params.cardConfig; + this._sectionConfig = params.sectionConfig; if (!Object.isFrozen(this._cardConfig)) { this._cardConfig = deepFreeze(this._cardConfig); } + if (!Object.isFrozen(this._sectionConfig)) { + this._sectionConfig = deepFreeze(this._sectionConfig); + } if (this._yamlEditor) { this._yamlEditor.setValue(this._cardConfig); } @@ -42,6 +55,45 @@ export class HuiDialogSuggestCard extends LitElement { fireEvent(this, "dialog-closed", { dialog: this.localName }); } + private get _viewSupportsSection(): boolean { + if (!this._params?.lovelaceConfig || !this._params?.path) { + return false; + } + + const { viewIndex } = parseLovelaceContainerPath(this._params.path); + const viewConfig = this._params!.lovelaceConfig.views[viewIndex]; + + return !isStrategyView(viewConfig) && viewConfig.type === "sections"; + } + + private _renderPreview() { + if (this._sectionConfig && this._viewSupportsSection) { + return html` +
    + +
    + `; + } + if (this._cardConfig) { + return html` +
    + ${this._cardConfig.map( + (cardConfig) => html` + + ` + )} +
    + `; + } + return nothing; + } + protected render() { if (!this._params) { return nothing; @@ -56,20 +108,7 @@ export class HuiDialogSuggestCard extends LitElement { )} >
    - ${this._cardConfig - ? html` -
    - ${this._cardConfig.map( - (cardConfig) => html` - - ` - )} -
    - ` - : ""} + ${this._renderPreview()} ${this._params.yaml && this._cardConfig ? html`
    @@ -79,7 +118,7 @@ export class HuiDialogSuggestCard extends LitElement { >
    ` - : ""} + : nothing}
    { if ( !this._params?.lovelaceConfig || @@ -188,13 +254,12 @@ export class HuiDialogSuggestCard extends LitElement { return; } this._saving = true; - await this._params!.saveConfig( - addCards( - this._params!.lovelaceConfig, - this._params!.path as [number], - this._cardConfig - ) + + const newConfig = this._computeNewConfig( + this._params.lovelaceConfig, + this._params.path ); + await this._params!.saveConfig(newConfig); this._saving = false; showSaveSuccessToast(this, this.hass); this.closeDialog(); diff --git a/src/panels/lovelace/editor/card-editor/hui-section-preview.ts b/src/panels/lovelace/editor/card-editor/hui-section-preview.ts new file mode 100644 index 0000000000..c569fcf4c1 --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/hui-section-preview.ts @@ -0,0 +1,104 @@ +import { PropertyValues, ReactiveElement } from "lit"; +import { customElement, property } from "lit/decorators"; +import { LovelaceSectionElement } from "../../../../data/lovelace"; +import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; +import { HomeAssistant } from "../../../../types"; +import { createSectionElement } from "../../create-element/create-section-element"; +import { createErrorSectionConfig } from "../../sections/hui-error-section"; +import { LovelaceConfig } from "../../../../data/lovelace/config/types"; + +@customElement("hui-section-preview") +export class HuiSectionPreview extends ReactiveElement { + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public lovelace?: LovelaceConfig; + + @property({ attribute: false }) public config?: LovelaceSectionConfig; + + private _element?: LovelaceSectionElement; + + private get _error() { + return this._element?.tagName === "HUI-ERROR-SECTION"; + } + + constructor() { + super(); + this.addEventListener("ll-rebuild", () => { + this._cleanup(); + if (this.config) { + this._createSection(this.config); + } + }); + } + + protected createRenderRoot() { + return this; + } + + protected update(changedProperties: PropertyValues) { + super.update(changedProperties); + + if (changedProperties.has("config")) { + const oldConfig = changedProperties.get("config") as + | undefined + | LovelaceSectionConfig; + + if (!this.config) { + this._cleanup(); + return; + } + + if (!this.config.type) { + this._createSection(createErrorSectionConfig("No section type found")); + return; + } + + if (!this._element) { + this._createSection(this.config); + return; + } + + // in case the element was an error element we always want to recreate it + if (!this._error && oldConfig && this.config.type === oldConfig.type) { + try { + this._element.setConfig(this.config); + } catch (err: any) { + this._createSection(createErrorSectionConfig(err.message)); + } + } else { + this._createSection(this.config); + } + } + + if (changedProperties.has("hass")) { + if (this._element) { + this._element.hass = this.hass; + } + } + } + + private _createSection(configValue: LovelaceSectionConfig): void { + this._cleanup(); + this._element = createSectionElement(configValue) as LovelaceSectionElement; + + if (this.hass) { + this._element!.hass = this.hass; + } + + this.appendChild(this._element!); + } + + private _cleanup() { + if (!this._element) { + return; + } + this.removeChild(this._element); + this._element = undefined; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-section-preview": HuiSectionPreview; + } +} diff --git a/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts index 230047d399..96c58879b2 100644 --- a/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts @@ -1,10 +1,12 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import type { LovelaceConfig } from "../../../../data/lovelace/config/types"; +import { LovelaceContainerPath } from "../lovelace-path"; export interface CreateCardDialogParams { lovelaceConfig: LovelaceConfig; saveConfig: (config: LovelaceConfig) => void; - path: [number]; + path: LovelaceContainerPath; + suggestedCards?: string[]; entities?: string[]; // We can pass entity id's that will be added to the config when a card is picked } diff --git a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts index 46070fd7ff..40391f6a13 100644 --- a/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-edit-card-dialog.ts @@ -1,14 +1,20 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; import type { LovelaceConfig } from "../../../../data/lovelace/config/types"; +import { LovelaceContainerPath } from "../lovelace-path"; -export interface EditCardDialogParams { +export type EditCardDialogParams = { lovelaceConfig: LovelaceConfig; saveConfig: (config: LovelaceConfig) => void; - path: [number, number | null]; - // If specified, the card will be replaced with the new card. - newCardConfig?: LovelaceCardConfig; -} + path: LovelaceContainerPath; +} & ( + | { + cardIndex: number; + } + | { + cardConfig: LovelaceCardConfig; + } +); export const importEditCardDialog = () => import("./hui-dialog-edit-card"); diff --git a/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts index ea43443d06..c95fed399e 100644 --- a/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts +++ b/src/panels/lovelace/editor/card-editor/show-suggest-card-dialog.ts @@ -1,14 +1,17 @@ import { fireEvent } from "../../../../common/dom/fire_event"; import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; +import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section"; import { LovelaceConfig } from "../../../../data/lovelace/config/types"; +import { LovelaceContainerPath } from "../lovelace-path"; export interface SuggestCardDialogParams { lovelaceConfig?: LovelaceConfig; yaml?: boolean; saveConfig?: (config: LovelaceConfig) => void; - path?: [number]; + path?: LovelaceContainerPath; entities?: string[]; // We pass this to create dialog when user chooses "Pick own" - cardConfig: LovelaceCardConfig[]; // We can pass a suggested config + cardConfig: LovelaceCardConfig[]; // We can pass a suggested config,s + sectionConfig?: LovelaceSectionConfig; } const importSuggestCardDialog = () => import("./hui-dialog-suggest-card"); diff --git a/src/panels/lovelace/editor/config-util.ts b/src/panels/lovelace/editor/config-util.ts index f494544fb6..09d2e611fa 100644 --- a/src/panels/lovelace/editor/config-util.ts +++ b/src/panels/lovelace/editor/config-util.ts @@ -1,296 +1,160 @@ import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { LovelaceSectionRawConfig } from "../../../data/lovelace/config/section"; import { LovelaceConfig } from "../../../data/lovelace/config/types"; import { LovelaceViewConfig, isStrategyView, } from "../../../data/lovelace/config/view"; import type { HomeAssistant } from "../../../types"; +import { + LovelaceCardPath, + LovelaceContainerPath, + findLovelaceCards, + findLovelaceContainer, + getLovelaceContainerPath, + parseLovelaceCardPath, + parseLovelaceContainerPath, + updateLovelaceCards, + updateLovelaceContainer, +} from "./lovelace-path"; export const addCard = ( config: LovelaceConfig, - path: [number], + path: LovelaceContainerPath, cardConfig: LovelaceCardConfig ): LovelaceConfig => { - const [viewIndex] = path; - const views: LovelaceViewConfig[] = []; - - config.views.forEach((viewConf, index) => { - if (index !== viewIndex) { - views.push(config.views[index]); - return; - } - - if (isStrategyView(viewConf)) { - throw new Error("You cannot add a card in a strategy view."); - } - - const cards = viewConf.cards - ? [...viewConf.cards, cardConfig] - : [cardConfig]; - - views.push({ - ...viewConf, - cards, - }); - }); - - return { - ...config, - views, - }; + const cards = findLovelaceCards(config, path); + const newCards = cards ? [...cards, cardConfig] : [cardConfig]; + const newConfig = updateLovelaceCards(config, path, newCards); + return newConfig; }; export const addCards = ( config: LovelaceConfig, - path: [number], + path: LovelaceContainerPath, cardConfigs: LovelaceCardConfig[] ): LovelaceConfig => { - const [viewIndex] = path; - const views: LovelaceViewConfig[] = []; - - config.views.forEach((viewConf, index) => { - if (index !== viewIndex) { - views.push(config.views[index]); - return; - } - - if (isStrategyView(viewConf)) { - throw new Error("You cannot add cards in a strategy view."); - } - - const cards = viewConf.cards - ? [...viewConf.cards, ...cardConfigs] - : [...cardConfigs]; - - views.push({ - ...viewConf, - cards, - }); - }); - - return { - ...config, - views, - }; + const cards = findLovelaceCards(config, path); + const newCards = cards ? [...cards, ...cardConfigs] : [...cardConfigs]; + const newConfig = updateLovelaceCards(config, path, newCards); + return newConfig; }; export const replaceCard = ( config: LovelaceConfig, - path: [number, number], + path: LovelaceCardPath, cardConfig: LovelaceCardConfig ): LovelaceConfig => { - const [viewIndex, cardIndex] = path; - const views: LovelaceViewConfig[] = []; + const { cardIndex } = parseLovelaceCardPath(path); + const containerPath = getLovelaceContainerPath(path); - config.views.forEach((viewConf, index) => { - if (index !== viewIndex) { - views.push(config.views[index]); - return; - } + const cards = findLovelaceCards(config, containerPath); - if (isStrategyView(viewConf)) { - throw new Error("You cannot replace a card in a strategy view."); - } + const newCards = (cards ?? []).map((origConf, ind) => + ind === cardIndex ? cardConfig : origConf + ); - views.push({ - ...viewConf, - cards: (viewConf.cards || []).map((origConf, ind) => - ind === cardIndex ? cardConfig : origConf - ), - }); - }); - - return { - ...config, - views, - }; + const newConfig = updateLovelaceCards(config, containerPath, newCards); + return newConfig; }; export const deleteCard = ( config: LovelaceConfig, - path: [number, number] + path: LovelaceCardPath ): LovelaceConfig => { - const [viewIndex, cardIndex] = path; - const views: LovelaceViewConfig[] = []; + const { cardIndex } = parseLovelaceCardPath(path); + const containerPath = getLovelaceContainerPath(path); - config.views.forEach((viewConf, index) => { - if (index !== viewIndex) { - views.push(config.views[index]); - return; - } + const cards = findLovelaceCards(config, containerPath); - if (isStrategyView(viewConf)) { - throw new Error("You cannot delete a card in a strategy view."); - } + const newCards = (cards ?? []).filter((_origConf, ind) => ind !== cardIndex); - views.push({ - ...viewConf, - cards: (viewConf.cards || []).filter( - (_origConf, ind) => ind !== cardIndex - ), - }); - }); - - return { - ...config, - views, - }; + const newConfig = updateLovelaceCards(config, containerPath, newCards); + return newConfig; }; export const insertCard = ( config: LovelaceConfig, - path: [number, number], + path: LovelaceCardPath, cardConfig: LovelaceCardConfig ) => { - const [viewIndex, cardIndex] = path; - const views: LovelaceViewConfig[] = []; + const { cardIndex } = parseLovelaceCardPath(path); + const containerPath = getLovelaceContainerPath(path); - config.views.forEach((viewConf, index) => { - if (index !== viewIndex) { - views.push(config.views[index]); - return; - } + const cards = findLovelaceCards(config, containerPath); - if (isStrategyView(viewConf)) { - throw new Error("You cannot insert a card in a strategy view."); - } + const newCards = cards + ? [...cards.slice(0, cardIndex), cardConfig, ...cards.slice(cardIndex)] + : [cardConfig]; - const cards = viewConf.cards - ? [ - ...viewConf.cards.slice(0, cardIndex), - cardConfig, - ...viewConf.cards.slice(cardIndex), - ] - : [cardConfig]; - - views.push({ - ...viewConf, - cards, - }); - }); - - return { - ...config, - views, - }; + const newConfig = updateLovelaceCards(config, containerPath, newCards); + return newConfig; }; -export const swapCard = ( +export const moveCardToIndex = ( config: LovelaceConfig, - path1: [number, number], - path2: [number, number] + path: LovelaceCardPath, + index: number ): LovelaceConfig => { - const origView1 = config.views[path1[0]]; - const origView2 = config.views[path2[0]]; + const { cardIndex } = parseLovelaceCardPath(path); + const containerPath = getLovelaceContainerPath(path); - if (isStrategyView(origView1) || isStrategyView(origView2)) { - throw new Error("You cannot move swap cards in a strategy view."); - } + const cards = findLovelaceCards(config, containerPath); - const card1 = origView1.cards![path1[1]]; - const card2 = origView2.cards![path2[1]]; + const newCards = cards ? [...cards] : []; - const newView1 = { - ...origView1, - cards: origView1.cards!.map((origCard, index) => - index === path1[1] ? card2 : origCard - ), - }; - - const updatedOrigView2 = path1[0] === path2[0] ? newView1 : origView2; - const newView2 = { - ...updatedOrigView2, - cards: updatedOrigView2.cards!.map((origCard, index) => - index === path2[1] ? card1 : origCard - ), - }; - - return { - ...config, - views: config.views.map((origView, index) => - index === path2[0] ? newView2 : index === path1[0] ? newView1 : origView - ), - }; -}; - -export const moveCardToPosition = ( - config: LovelaceConfig, - path: [number, number], - position: number -): LovelaceConfig => { - const view = config.views[path[0]]; - - if (isStrategyView(view)) { - throw new Error("You cannot move a card in a strategy view."); - } - - const oldIndex = path[1]; - const newIndex = Math.max(Math.min(position - 1, view.cards!.length - 1), 0); - - const newCards = [...view.cards!]; + const oldIndex = cardIndex; + const newIndex = Math.max(Math.min(index, newCards.length - 1), 0); const card = newCards[oldIndex]; newCards.splice(oldIndex, 1); newCards.splice(newIndex, 0, card); - const newView = { - ...view, - cards: newCards, - }; + const newConfig = updateLovelaceCards(config, containerPath, newCards); + return newConfig; +}; - return { - ...config, - views: config.views.map((origView, index) => - index === path[0] ? newView : origView - ), - }; +export const moveCardToContainer = ( + config: LovelaceConfig, + fromPath: LovelaceCardPath, + toPath: LovelaceContainerPath +): LovelaceConfig => { + const { + cardIndex: fromCardIndex, + viewIndex: fromViewIndex, + sectionIndex: fromSectionIndex, + } = parseLovelaceCardPath(fromPath); + const { viewIndex: toViewIndex, sectionIndex: toSectionIndex } = + parseLovelaceContainerPath(toPath); + + if (fromViewIndex === toViewIndex && fromSectionIndex === toSectionIndex) { + throw new Error("You cannot move a card to the view or section it is in."); + } + + const fromContainerPath = getLovelaceContainerPath(fromPath); + const cards = findLovelaceCards(config, fromContainerPath); + const card = cards![fromCardIndex]; + + let newConfig = addCard(config, toPath, card); + newConfig = deleteCard(newConfig, fromPath); + + return newConfig; }; export const moveCard = ( config: LovelaceConfig, - fromPath: [number, number], - toPath: [number] + fromPath: LovelaceCardPath, + toPath: LovelaceCardPath ): LovelaceConfig => { - if (fromPath[0] === toPath[0]) { - throw new Error("You cannot move a card to the view it is in."); - } - const fromView = config.views[fromPath[0]]; - const toView = config.views[toPath[0]]; + const { cardIndex: fromCardIndex } = parseLovelaceCardPath(fromPath); + const fromContainerPath = getLovelaceContainerPath(fromPath); + const cards = findLovelaceCards(config, fromContainerPath); + const card = cards![fromCardIndex]; - if (isStrategyView(fromView)) { - throw new Error("You cannot move a card from a strategy view."); - } + let newConfig = deleteCard(config, fromPath); + newConfig = insertCard(newConfig, toPath, card); - if (isStrategyView(toView)) { - throw new Error("You cannot move a card to a strategy view."); - } - - const card = fromView.cards![fromPath[1]]; - - const newView1 = { - ...fromView, - cards: (fromView.cards || []).filter( - (_origConf, ind) => ind !== fromPath[1] - ), - }; - - const cards = toView.cards ? [...toView.cards, card] : [card]; - - const newView2 = { - ...toView, - cards, - }; - - return { - ...config, - views: config.views.map((origView, index) => - index === toPath[0] - ? newView2 - : index === fromPath[0] - ? newView1 - : origView - ), - }; + return newConfig; }; export const addView = ( @@ -356,3 +220,84 @@ export const deleteView = ( ...config, views: config.views.filter((_origView, index) => index !== viewIndex), }); + +export const addSection = ( + config: LovelaceConfig, + viewIndex: number, + sectionConfig: LovelaceSectionRawConfig +): LovelaceConfig => { + const view = findLovelaceContainer(config, [viewIndex]) as LovelaceViewConfig; + if (isStrategyView(view)) { + throw new Error("Deleting sections in a strategy is not supported."); + } + const sections = view.sections + ? [...view.sections, sectionConfig] + : [sectionConfig]; + + const newConfig = updateLovelaceContainer(config, [viewIndex], { + ...view, + sections, + }); + return newConfig; +}; + +export const deleteSection = ( + config: LovelaceConfig, + viewIndex: number, + sectionIndex: number +): LovelaceConfig => { + const view = findLovelaceContainer(config, [viewIndex]) as LovelaceViewConfig; + if (isStrategyView(view)) { + throw new Error("Deleting sections in a strategy is not supported."); + } + const sections = view.sections?.filter( + (_origSection, index) => index !== sectionIndex + ); + + const newConfig = updateLovelaceContainer(config, [viewIndex], { + ...view, + sections, + }); + return newConfig; +}; + +export const insertSection = ( + config: LovelaceConfig, + viewIndex: number, + sectionIndex: number, + sectionConfig: LovelaceSectionRawConfig +): LovelaceConfig => { + const view = findLovelaceContainer(config, [viewIndex]) as LovelaceViewConfig; + if (isStrategyView(view)) { + throw new Error("Inserting sections in a strategy is not supported."); + } + const sections = view.sections + ? [ + ...view.sections.slice(0, sectionIndex), + sectionConfig, + ...view.sections.slice(sectionIndex), + ] + : [sectionConfig]; + + const newConfig = updateLovelaceContainer(config, [viewIndex], { + ...view, + sections, + }); + return newConfig; +}; + +export const moveSection = ( + config: LovelaceConfig, + fromPath: [number, number], + toPath: [number, number] +): LovelaceConfig => { + const section = findLovelaceContainer( + config, + fromPath + ) as LovelaceSectionRawConfig; + + let newConfig = deleteSection(config, fromPath[0], fromPath[1]); + newConfig = insertSection(newConfig, toPath[0], toPath[1], section); + + return newConfig; +}; diff --git a/src/panels/lovelace/editor/delete-card.ts b/src/panels/lovelace/editor/delete-card.ts index f4b2c08b09..f3264bc023 100644 --- a/src/panels/lovelace/editor/delete-card.ts +++ b/src/panels/lovelace/editor/delete-card.ts @@ -1,22 +1,29 @@ -import { isStrategyView } from "../../../data/lovelace/config/view"; import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; import { HomeAssistant } from "../../../types"; import { showDeleteSuccessToast } from "../../../util/toast-deleted-success"; import { Lovelace } from "../types"; import { showDeleteCardDialog } from "./card-editor/show-delete-card-dialog"; import { deleteCard, insertCard } from "./config-util"; +import { + LovelaceCardPath, + findLovelaceContainer, + getLovelaceContainerPath, + parseLovelaceCardPath, +} from "./lovelace-path"; export async function confDeleteCard( element: HTMLElement, hass: HomeAssistant, lovelace: Lovelace, - path: [number, number] + path: LovelaceCardPath ): Promise { - const view = lovelace.config.views[path[0]]; - if (isStrategyView(view)) { - throw new Error("Deleting cards in a strategy view is not supported."); + const containerPath = getLovelaceContainerPath(path); + const { cardIndex } = parseLovelaceCardPath(path); + const containerConfig = findLovelaceContainer(lovelace.config, containerPath); + if ("strategy" in containerConfig) { + throw new Error("Deleting cards in a strategy is not supported."); } - const cardConfig = view.cards![path[1]]; + const cardConfig = containerConfig.cards![cardIndex]; showDeleteCardDialog(element, { cardConfig, deleteCard: async () => { diff --git a/src/panels/lovelace/editor/lovelace-path.ts b/src/panels/lovelace/editor/lovelace-path.ts new file mode 100644 index 0000000000..2eaf9dadc6 --- /dev/null +++ b/src/panels/lovelace/editor/lovelace-path.ts @@ -0,0 +1,197 @@ +import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { + LovelaceSectionRawConfig, + isStrategySection, +} from "../../../data/lovelace/config/section"; +import { LovelaceConfig } from "../../../data/lovelace/config/types"; +import { + LovelaceViewRawConfig, + isStrategyView, +} from "../../../data/lovelace/config/view"; + +export type LovelaceCardPath = [number, number] | [number, number, number]; +export type LovelaceContainerPath = [number] | [number, number]; + +export const parseLovelaceCardPath = ( + path: LovelaceCardPath +): { viewIndex: number; sectionIndex?: number; cardIndex: number } => { + if (path.length === 2) { + return { + viewIndex: path[0], + cardIndex: path[1], + }; + } + return { + viewIndex: path[0], + sectionIndex: path[1], + cardIndex: path[2], + }; +}; + +export const parseLovelaceContainerPath = ( + path: LovelaceContainerPath +): { viewIndex: number; sectionIndex?: number } => { + if (path.length === 1) { + return { + viewIndex: path[0], + }; + } + return { + viewIndex: path[0], + sectionIndex: path[1], + }; +}; + +export const getLovelaceContainerPath = ( + path: LovelaceCardPath +): LovelaceContainerPath => path.slice(0, -1) as LovelaceContainerPath; + +export const findLovelaceContainer = ( + config: LovelaceConfig, + path: LovelaceContainerPath +): LovelaceViewRawConfig | LovelaceSectionRawConfig => { + const { viewIndex, sectionIndex } = parseLovelaceContainerPath(path); + + const view = config.views[viewIndex]; + + if (!view) { + throw new Error("View does not exist"); + } + if (sectionIndex === undefined) { + return view; + } + if (isStrategyView(view)) { + throw new Error("Can not find section in a strategy view"); + } + + const section = view.sections?.[sectionIndex]; + + if (!section) { + throw new Error("Section does not exist"); + } + return section; +}; + +export const findLovelaceCards = ( + config: LovelaceConfig, + path: LovelaceContainerPath +): LovelaceCardConfig[] | undefined => { + const { viewIndex, sectionIndex } = parseLovelaceContainerPath(path); + + const view = config.views[viewIndex]; + + if (!view) { + throw new Error("View does not exist"); + } + if (isStrategyView(view)) { + throw new Error("Can not find cards in a strategy view"); + } + if (sectionIndex === undefined) { + return view.cards; + } + + const section = view.sections?.[sectionIndex]; + + if (!section) { + throw new Error("Section does not exist"); + } + if (isStrategySection(section)) { + throw new Error("Can not find cards in a strategy section"); + } + return section.cards; +}; + +export const updateLovelaceContainer = ( + config: LovelaceConfig, + path: LovelaceContainerPath, + containerConfig: LovelaceViewRawConfig | LovelaceSectionRawConfig +): LovelaceConfig => { + const { viewIndex, sectionIndex } = parseLovelaceContainerPath(path); + + let updated = false; + const newViews = config.views.map((view, vIndex) => { + if (vIndex !== viewIndex) return view; + + if (sectionIndex === undefined) { + updated = true; + return containerConfig; + } + + if (isStrategyView(view)) { + throw new Error("Can not update section in a strategy view"); + } + + if (view.sections === undefined) { + throw new Error("Section does not exist"); + } + + const newSections = view.sections.map((section, sIndex) => { + if (sIndex !== sectionIndex) return section; + updated = true; + return containerConfig; + }); + return { + ...view, + sections: newSections, + }; + }); + + if (!updated) { + throw new Error("Can not update cards in a non-existing view/section"); + } + return { + ...config, + views: newViews, + }; +}; + +export const updateLovelaceCards = ( + config: LovelaceConfig, + path: LovelaceContainerPath, + cards: LovelaceCardConfig[] +): LovelaceConfig => { + const { viewIndex, sectionIndex } = parseLovelaceContainerPath(path); + + let updated = false; + const newViews = config.views.map((view, vIndex) => { + if (vIndex !== viewIndex) return view; + if (isStrategyView(view)) { + throw new Error("Can not update cards in a strategy view"); + } + if (sectionIndex === undefined) { + updated = true; + return { + ...view, + cards, + }; + } + + if (view.sections === undefined) { + throw new Error("Section does not exist"); + } + + const newSections = view.sections.map((section, sIndex) => { + if (sIndex !== sectionIndex) return section; + if (isStrategySection(section)) { + throw new Error("Can not update cards in a strategy section"); + } + updated = true; + return { + ...section, + cards, + }; + }); + return { + ...view, + sections: newSections, + }; + }); + + if (!updated) { + throw new Error("Can not update cards in a non-existing view/section"); + } + return { + ...config, + views: newViews, + }; +}; diff --git a/src/panels/lovelace/editor/types.ts b/src/panels/lovelace/editor/types.ts index 1edab843d1..44c42beac8 100644 --- a/src/panels/lovelace/editor/types.ts +++ b/src/panels/lovelace/editor/types.ts @@ -62,6 +62,7 @@ export interface Card { description?: string; showElement?: boolean; isCustom?: boolean; + isSuggested?: boolean; } export interface HeaderFooter { diff --git a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts index 98cd3f3163..dadd34449e 100644 --- a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts +++ b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts @@ -21,7 +21,10 @@ import "../card-editor/hui-entity-picker-table"; import { showSuggestCardDialog } from "../card-editor/show-suggest-card-dialog"; import { showSelectViewDialog } from "../select-view/show-select-view-dialog"; import { LovelaceConfig } from "../../../../data/lovelace/config/types"; -import { computeCards } from "../../common/generate-lovelace-config"; +import { + computeCards, + computeSection, +} from "../../common/generate-lovelace-config"; @customElement("hui-unused-entities") export class HuiUnusedEntities extends LitElement { @@ -132,6 +135,8 @@ export class HuiUnusedEntities extends LitElement { this._selectedEntities, {} ); + const sectionConfig = computeSection(this._selectedEntities, {}); + if (this.lovelace.config.views.length === 1) { showSuggestCardDialog(this, { lovelaceConfig: this.lovelace.config!, @@ -139,6 +144,7 @@ export class HuiUnusedEntities extends LitElement { path: [0], entities: this._selectedEntities, cardConfig, + sectionConfig, }); return; } @@ -152,6 +158,7 @@ export class HuiUnusedEntities extends LitElement { path: [viewIndex], entities: this._selectedEntities, cardConfig, + sectionConfig, }); }, }); diff --git a/src/panels/lovelace/editor/view-editor/hui-view-editor.ts b/src/panels/lovelace/editor/view-editor/hui-view-editor.ts index b693848a8d..3c4326d394 100644 --- a/src/panels/lovelace/editor/view-editor/hui-view-editor.ts +++ b/src/panels/lovelace/editor/view-editor/hui-view-editor.ts @@ -6,13 +6,14 @@ import { slugify } from "../../../../common/string/slugify"; import type { LocalizeFunc } from "../../../../common/translations/localize"; import "../../../../components/ha-form/ha-form"; import type { SchemaUnion } from "../../../../components/ha-form/types"; +import { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; import type { HomeAssistant } from "../../../../types"; import { DEFAULT_VIEW_LAYOUT, + SECTION_VIEW_LAYOUT, PANEL_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT, } from "../../views/const"; -import { LovelaceViewConfig } from "../../../../data/lovelace/config/view"; declare global { interface HASSDomEvents { @@ -53,6 +54,7 @@ export class HuiViewEditor extends LitElement { DEFAULT_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT, PANEL_VIEW_LAYOUT, + SECTION_VIEW_LAYOUT, ] as const ).map((type) => ({ value: type, diff --git a/src/panels/lovelace/sections/const.ts b/src/panels/lovelace/sections/const.ts new file mode 100644 index 0000000000..8551f0fe92 --- /dev/null +++ b/src/panels/lovelace/sections/const.ts @@ -0,0 +1,2 @@ +export const GRID_SECTION_LAYOUT = "grid"; +export const DEFAULT_SECTION_LAYOUT = GRID_SECTION_LAYOUT; diff --git a/src/panels/lovelace/sections/hui-error-section.ts b/src/panels/lovelace/sections/hui-error-section.ts new file mode 100644 index 0000000000..801f6f0379 --- /dev/null +++ b/src/panels/lovelace/sections/hui-error-section.ts @@ -0,0 +1,60 @@ +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import "../../../components/ha-label-badge"; +import "../../../components/ha-svg-icon"; +import { LovelaceSectionElement } from "../../../data/lovelace"; +import { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; +import { HomeAssistant } from "../../../types"; + +export interface ErrorSectionConfig extends LovelaceSectionConfig { + error: string; +} + +export const createErrorSectionElement = (config: ErrorSectionConfig) => { + const el = document.createElement( + "hui-error-section" + ) as LovelaceSectionElement; + el.setConfig(config); + return el; +}; + +export const createErrorSectionConfig = ( + error: string +): ErrorSectionConfig => ({ + type: "error", + error, +}); + +@customElement("hui-error-section") +export class HuiErrorSection + extends LitElement + implements LovelaceSectionElement +{ + public hass?: HomeAssistant; + + @property({ type: Boolean }) public isStrategy = false; + + @state() private _config?: ErrorSectionConfig; + + public setConfig(config: ErrorSectionConfig): void { + this._config = config; + } + + protected render() { + if (!this._config) { + return nothing; + } + + // Todo improve + return html` +

    Error

    +

    ${this._config.error}

    + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-error-section": HuiErrorSection; + } +} diff --git a/src/panels/lovelace/sections/hui-grid-section.ts b/src/panels/lovelace/sections/hui-grid-section.ts new file mode 100644 index 0000000000..1a6ed30ff7 --- /dev/null +++ b/src/panels/lovelace/sections/hui-grid-section.ts @@ -0,0 +1,246 @@ +import { mdiPlus } from "@mdi/js"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; +import { property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; +import { repeat } from "lit/directives/repeat"; +import { styleMap } from "lit/directives/style-map"; +import { fireEvent } from "../../../common/dom/fire_event"; +import type { HaSortableOptions } from "../../../components/ha-sortable"; +import { LovelaceSectionElement } from "../../../data/lovelace"; +import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; +import { haStyle } from "../../../resources/styles"; +import type { HomeAssistant } from "../../../types"; +import { HuiErrorCard } from "../cards/hui-error-card"; +import "../components/hui-card-edit-mode"; +import { moveCard } from "../editor/config-util"; +import type { Lovelace, LovelaceCard } from "../types"; + +const CARD_SORTABLE_OPTIONS: HaSortableOptions = { + delay: 200, + delayOnTouchOnly: true, + direction: "vertical", + invertedSwapThreshold: 0.7, +} as HaSortableOptions; + +export class GridSection extends LitElement implements LovelaceSectionElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public lovelace?: Lovelace; + + @property({ type: Number }) public index?: number; + + @property({ type: Number }) public viewIndex?: number; + + @property({ type: Boolean }) public isStrategy = false; + + @property({ attribute: false }) public cards: Array< + LovelaceCard | HuiErrorCard + > = []; + + @state() _config?: LovelaceSectionConfig; + + @state() _dragging = false; + + public setConfig(config: LovelaceSectionConfig): void { + this._config = config; + } + + private _cardConfigKeys = new WeakMap(); + + private _getKey(cardConfig: LovelaceCardConfig) { + if (!this._cardConfigKeys.has(cardConfig)) { + this._cardConfigKeys.set(cardConfig, Math.random().toString()); + } + return this._cardConfigKeys.get(cardConfig)!; + } + + render() { + if (!this.cards || !this._config) return nothing; + + const cardsConfig = this._config?.cards ?? []; + + const editMode = Boolean(this.lovelace?.editMode && !this.isStrategy); + + return html` + ${this._config.title || this.lovelace?.editMode + ? html` +

    + ${this._config.title || + this.hass.localize( + "ui.panel.lovelace.editor.section.unnamed_section" + )} +

    + ` + : nothing} + +
    + ${repeat( + cardsConfig, + (cardConfig) => this._getKey(cardConfig), + (_cardConfig, idx) => { + const card = this.cards![idx]; + (card as any).editMode = editMode; + const size = card && (card as any).getGridSize?.(); + return html` +
    + ${editMode + ? html` + + ${card} + + ` + : card} +
    + `; + } + )} + ${editMode + ? html` + + ` + : nothing} +
    +
    + `; + } + + private _cardMoved(ev) { + ev.stopPropagation(); + const { oldIndex, newIndex, oldPath, newPath } = ev.detail; + const newConfig = moveCard( + this.lovelace!.config, + [...oldPath, oldIndex] as [number, number, number], + [...newPath, newIndex] as [number, number, number] + ); + this.lovelace!.saveConfig(newConfig); + } + + private _dragStart() { + this._dragging = true; + } + + private _dragEnd() { + this._dragging = false; + } + + private _addCard() { + fireEvent(this, "ll-create-card", { suggested: ["tile"] }); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + display: flex; + flex-direction: column; + gap: 8px; + } + .container { + --column-count: 4; + display: grid; + grid-template-columns: repeat(var(--column-count), minmax(0, 1fr)); + grid-auto-rows: minmax(66px, auto); + gap: 8px; + padding: 0; + margin: 0 auto; + } + + .container.edit-mode { + padding: 8px; + border-radius: var(--ha-card-border-radius, 12px); + border: 2px dashed var(--divider-color); + min-height: 66px; + } + + .title { + color: var(--primary-text-color); + font-size: 20px; + font-weight: normal; + margin: 0px; + letter-spacing: 0.1px; + line-height: 32px; + min-height: 32px; + display: block; + padding: 24px 10px 10px; + } + + .title.placeholder { + color: var(--secondary-text-color); + font-style: italic; + } + + .card { + border-radius: var(--ha-card-border-radius, 12px); + position: relative; + grid-row: span var(--row-size, 1); + grid-column: span var(--column-size, 4); + } + + .add { + outline: none; + grid-row: span var(--row-size, 1); + grid-column: span var(--column-size, 2); + background: none; + cursor: pointer; + border-radius: var(--ha-card-border-radius, 12px); + border: 2px dashed var(--primary-color); + height: 66px; + order: 1; + } + .add:focus { + border-style: solid; + } + .sortable-ghost { + border-radius: var(--ha-card-border-radius, 12px); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-grid-section": GridSection; + } +} + +customElements.define("hui-grid-section", GridSection); diff --git a/src/panels/lovelace/sections/hui-section.ts b/src/panels/lovelace/sections/hui-section.ts new file mode 100644 index 0000000000..b50b1df18a --- /dev/null +++ b/src/panels/lovelace/sections/hui-section.ts @@ -0,0 +1,247 @@ +import { PropertyValues, ReactiveElement } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import "../../../components/ha-svg-icon"; +import type { LovelaceSectionElement } from "../../../data/lovelace"; +import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { + LovelaceSectionConfig, + LovelaceSectionRawConfig, + isStrategySection, +} from "../../../data/lovelace/config/section"; +import type { HomeAssistant } from "../../../types"; +import type { HuiErrorCard } from "../cards/hui-error-card"; +import { createCardElement } from "../create-element/create-card-element"; +import { + createErrorCardConfig, + createErrorCardElement, +} from "../create-element/create-element-base"; +import { createSectionElement } from "../create-element/create-section-element"; +import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog"; +import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; +import { deleteCard } from "../editor/config-util"; +import { confDeleteCard } from "../editor/delete-card"; +import { parseLovelaceCardPath } from "../editor/lovelace-path"; +import { generateLovelaceSectionStrategy } from "../strategies/get-strategy"; +import type { Lovelace, LovelaceCard } from "../types"; +import { DEFAULT_SECTION_LAYOUT } from "./const"; + +@customElement("hui-section") +export class HuiSection extends ReactiveElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public lovelace!: Lovelace; + + @property({ attribute: false }) public config!: LovelaceSectionRawConfig; + + @property({ type: Number }) public index!: number; + + @property({ type: Number }) public viewIndex!: number; + + @state() private _cards: Array = []; + + private _layoutElementType?: string; + + private _layoutElement?: LovelaceSectionElement; + + // Public to make demo happy + public createCardElement(cardConfig: LovelaceCardConfig) { + const element = createCardElement(cardConfig) as LovelaceCard; + try { + element.hass = this.hass; + } catch (e: any) { + return createErrorCardElement( + createErrorCardConfig(e.message, cardConfig) + ); + } + element.addEventListener( + "ll-rebuild", + (ev: Event) => { + // In edit mode let it go to hui-root and rebuild whole section. + if (!this.lovelace!.editMode) { + ev.stopPropagation(); + this._rebuildCard(element, cardConfig); + } + }, + { once: true } + ); + return element; + } + + protected createRenderRoot() { + return this; + } + + public willUpdate(changedProperties: PropertyValues): void { + super.willUpdate(changedProperties); + + /* + We need to handle the following use cases: + - initialization: create layout element, populate + - config changed to section with same layout element + - config changed to section with different layout element + - forwarded properties hass/narrow/lovelace/cards change + - cards change if one is rebuild when it was loaded later + - lovelace changes if edit mode is enabled or config has changed + */ + + const oldConfig = changedProperties.get("config"); + + // If config has changed, create element if necessary and set all values. + if ( + changedProperties.has("config") && + (!oldConfig || this.config !== oldConfig) + ) { + this._initializeConfig(); + } + } + + protected update(changedProperties) { + super.update(changedProperties); + + // If no layout element, we're still creating one + if (this._layoutElement) { + // Config has not changed. Just props + if (changedProperties.has("hass")) { + this._cards.forEach((element) => { + try { + element.hass = this.hass; + } catch (e: any) { + this._rebuildCard(element, createErrorCardConfig(e.message, null)); + } + }); + + this._layoutElement.hass = this.hass; + } + if (changedProperties.has("lovelace")) { + this._layoutElement.lovelace = this.lovelace; + } + if (changedProperties.has("_cards")) { + this._layoutElement.cards = this._cards; + } + } + } + + private async _initializeConfig() { + let sectionConfig = { ...this.config }; + let isStrategy = false; + + if (isStrategySection(sectionConfig)) { + isStrategy = true; + sectionConfig = await generateLovelaceSectionStrategy( + sectionConfig.strategy, + this.hass! + ); + } + + sectionConfig = { + ...sectionConfig, + type: sectionConfig.type || DEFAULT_SECTION_LAYOUT, + }; + + // Create a new layout element if necessary. + let addLayoutElement = false; + + if ( + !this._layoutElement || + this._layoutElementType !== sectionConfig.type + ) { + addLayoutElement = true; + this._createLayoutElement(sectionConfig); + } + + this._createCards(sectionConfig); + this._layoutElement!.isStrategy = isStrategy; + this._layoutElement!.hass = this.hass; + this._layoutElement!.lovelace = this.lovelace; + this._layoutElement!.index = this.index; + this._layoutElement!.viewIndex = this.viewIndex; + this._layoutElement!.cards = this._cards; + + if (addLayoutElement) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + this.appendChild(this._layoutElement!); + } + } + + private _createLayoutElement(config: LovelaceSectionConfig): void { + this._layoutElement = createSectionElement( + config + ) as LovelaceSectionElement; + this._layoutElementType = config.type; + this._layoutElement.addEventListener("ll-create-card", (ev) => { + ev.stopPropagation(); + showCreateCardDialog(this, { + lovelaceConfig: this.lovelace.config, + saveConfig: this.lovelace.saveConfig, + path: [this.viewIndex, this.index], + suggestedCards: ev.detail?.suggested, + }); + }); + this._layoutElement.addEventListener("ll-edit-card", (ev) => { + ev.stopPropagation(); + const { cardIndex } = parseLovelaceCardPath(ev.detail.path); + showEditCardDialog(this, { + lovelaceConfig: this.lovelace.config, + saveConfig: this.lovelace.saveConfig, + path: [this.viewIndex, this.index], + cardIndex, + }); + }); + this._layoutElement.addEventListener("ll-delete-card", (ev) => { + ev.stopPropagation(); + if (ev.detail.confirm) { + confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path); + } else { + const newLovelace = deleteCard(this.lovelace!.config, ev.detail.path); + this.lovelace.saveConfig(newLovelace); + } + }); + } + + private _createCards(config: LovelaceSectionConfig): void { + if (!config || !config.cards || !Array.isArray(config.cards)) { + this._cards = []; + return; + } + + this._cards = config.cards.map((cardConfig) => { + const element = this.createCardElement(cardConfig); + try { + element.hass = this.hass; + } catch (e: any) { + return createErrorCardElement( + createErrorCardConfig(e.message, cardConfig) + ); + } + return element; + }); + } + + private _rebuildCard( + cardElToReplace: LovelaceCard, + config: LovelaceCardConfig + ): void { + let newCardEl = this.createCardElement(config); + try { + newCardEl.hass = this.hass; + } catch (e: any) { + newCardEl = createErrorCardElement( + createErrorCardConfig(e.message, config) + ); + } + if (cardElToReplace.parentElement) { + cardElToReplace.parentElement!.replaceChild(newCardEl, cardElToReplace); + } + this._cards = this._cards!.map((curCardEl) => + curCardEl === cardElToReplace ? newCardEl : curCardEl + ); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-section": HuiSection; + } +} diff --git a/src/panels/lovelace/strategies/get-strategy.ts b/src/panels/lovelace/strategies/get-strategy.ts index ee62115275..927089dedd 100644 --- a/src/panels/lovelace/strategies/get-strategy.ts +++ b/src/panels/lovelace/strategies/get-strategy.ts @@ -12,6 +12,7 @@ import { AsyncReturnType, HomeAssistant } from "../../../types"; import { cleanLegacyStrategyConfig, isLegacyStrategy } from "./legacy-strategy"; import { LovelaceDashboardStrategy, + LovelaceSectionStrategy, LovelaceStrategy, LovelaceViewStrategy, } from "./types"; @@ -27,13 +28,15 @@ const STRATEGIES: Record> = { "original-states": () => import("./original-states-view-strategy"), energy: () => import("../../energy/strategies/energy-view-strategy"), }, + section: {}, }; -export type LovelaceStrategyConfigType = "dashboard" | "view"; +export type LovelaceStrategyConfigType = "dashboard" | "view" | "section"; type Strategies = { dashboard: LovelaceDashboardStrategy; view: LovelaceViewStrategy; + section: LovelaceSectionStrategy; }; type StrategyConfig = AsyncReturnType< @@ -163,6 +166,24 @@ export const generateLovelaceViewStrategy = async ( hass ); +export const generateLovelaceSectionStrategy = async ( + strategyConfig: LovelaceStrategyConfig, + hass: HomeAssistant +): Promise => + generateStrategy( + "section", + (err) => ({ + cards: [ + { + type: "markdown", + content: `Error loading the section strategy:\n> ${err}`, + }, + ], + }), + strategyConfig, + hass + ); + /** * Find all references to strategies and replaces them with the generated output */ @@ -175,11 +196,24 @@ export const expandLovelaceConfigStrategies = async ( : { ...config }; newConfig.views = await Promise.all( - newConfig.views.map((view) => - isStrategyView(view) - ? generateLovelaceViewStrategy(view.strategy, hass) - : view - ) + newConfig.views.map(async (view) => { + const newView = isStrategyView(view) + ? await generateLovelaceViewStrategy(view.strategy, hass) + : { ...view }; + + if (newView.sections) { + newView.sections = await Promise.all( + newView.sections.map(async (section) => { + const newSection = isStrategyView(section) + ? await generateLovelaceSectionStrategy(section.strategy, hass) + : { ...section }; + return newSection; + }) + ); + } + + return newView; + }) ); return newConfig; diff --git a/src/panels/lovelace/strategies/types.ts b/src/panels/lovelace/strategies/types.ts index da2ab8ee3c..ae1be1bbca 100644 --- a/src/panels/lovelace/strategies/types.ts +++ b/src/panels/lovelace/strategies/types.ts @@ -1,5 +1,6 @@ -import { LovelaceConfig } from "../../../data/lovelace/config/types"; +import { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy"; +import { LovelaceConfig } from "../../../data/lovelace/config/types"; import { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import { HomeAssistant } from "../../../types"; import { LovelaceGenericElementEditor } from "../types"; @@ -15,6 +16,9 @@ export interface LovelaceDashboardStrategy export interface LovelaceViewStrategy extends LovelaceStrategy {} +export interface LovelaceSectionStrategy + extends LovelaceStrategy {} + export interface LovelaceStrategyEditor extends LovelaceGenericElementEditor { setConfig(config: LovelaceStrategyConfig): void; } diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index b1d42984b8..31a87961f2 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -44,6 +44,7 @@ export interface LovelaceCard extends HTMLElement { isPanel?: boolean; editMode?: boolean; getCardSize(): number | Promise; + getGridSize?(): [number, number]; setConfig(config: LovelaceCardConfig): void; } diff --git a/src/panels/lovelace/views/const.ts b/src/panels/lovelace/views/const.ts index 5cc4709bbb..5633f05bbc 100644 --- a/src/panels/lovelace/views/const.ts +++ b/src/panels/lovelace/views/const.ts @@ -1,4 +1,9 @@ export const DEFAULT_VIEW_LAYOUT = "masonry"; export const PANEL_VIEW_LAYOUT = "panel"; export const SIDEBAR_VIEW_LAYOUT = "sidebar"; -export const VIEWS_NO_BADGE_SUPPORT = [PANEL_VIEW_LAYOUT, SIDEBAR_VIEW_LAYOUT]; +export const SECTION_VIEW_LAYOUT = "sections"; +export const VIEWS_NO_BADGE_SUPPORT = [ + PANEL_VIEW_LAYOUT, + SIDEBAR_VIEW_LAYOUT, + SECTION_VIEW_LAYOUT, +]; diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts new file mode 100644 index 0000000000..6e80a6266f --- /dev/null +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -0,0 +1,322 @@ +import { mdiArrowAll, mdiDelete, mdiPencil, mdiViewGridPlus } from "@mdi/js"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { repeat } from "lit/directives/repeat"; +import "../../../components/ha-icon-button"; +import "../../../components/ha-sortable"; +import "../../../components/ha-svg-icon"; +import type { LovelaceViewElement } from "../../../data/lovelace"; +import { LovelaceSectionConfig as LovelaceRawSectionConfig } from "../../../data/lovelace/config/section"; +import type { LovelaceViewConfig } from "../../../data/lovelace/config/view"; +import { + showConfirmationDialog, + showPromptDialog, +} from "../../../dialogs/generic/show-dialog-box"; +import type { HomeAssistant } from "../../../types"; +import { addSection, deleteSection, moveSection } from "../editor/config-util"; +import { + findLovelaceContainer, + updateLovelaceContainer, +} from "../editor/lovelace-path"; +import { HuiSection } from "../sections/hui-section"; +import type { Lovelace } from "../types"; + +@customElement("hui-sections-view") +export class SectionsView extends LitElement implements LovelaceViewElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public lovelace?: Lovelace; + + @property({ type: Number }) public index?: number; + + @property({ type: Boolean }) public isStrategy = false; + + @property({ attribute: false }) public sections: HuiSection[] = []; + + @state() private _config?: LovelaceViewConfig; + + public setConfig(config: LovelaceViewConfig): void { + this._config = config; + } + + private _sectionConfigKeys = new WeakMap(); + + private _getKey(sectionConfig: LovelaceRawSectionConfig) { + if (!this._sectionConfigKeys.has(sectionConfig)) { + this._sectionConfigKeys.set(sectionConfig, Math.random().toString()); + } + return this._sectionConfigKeys.get(sectionConfig)!; + } + + protected render() { + if (!this.lovelace) return nothing; + + const sectionsConfig = this._config?.sections ?? []; + + const editMode = this.lovelace.editMode; + + return html` + +
    + ${repeat( + sectionsConfig, + (sectionConfig) => this._getKey(sectionConfig), + (_sectionConfig, idx) => { + const section = this.sections[idx]; + (section as any).itemPath = [idx]; + return html` +
    + ${editMode + ? html` +
    +
    + + + +
    +
    + ` + : nothing} +
    ${section}
    +
    + `; + } + )} + ${editMode + ? html` + + ` + : nothing} +
    +
    + `; + } + + private _addSection(): void { + const newConfig = addSection(this.lovelace!.config, this.index!, { + type: "grid", + cards: [], + }); + this.lovelace!.saveConfig(newConfig); + } + + private async _editSection(ev) { + const index = ev.currentTarget.index; + + const path = [this.index!, index] as [number, number]; + + const section = findLovelaceContainer( + this.lovelace!.config, + path + ) as LovelaceRawSectionConfig; + + const newTitle = !section.title; + + const title = await showPromptDialog(this, { + title: this.hass.localize( + `ui.panel.lovelace.editor.edit_section_title.${newTitle ? "title_new" : "title"}` + ), + inputLabel: this.hass.localize( + "ui.panel.lovelace.editor.edit_section_title.input_label" + ), + inputType: "string", + defaultValue: section.title, + confirmText: newTitle + ? this.hass.localize("ui.common.add") + : this.hass.localize("ui.common.save"), + }); + + if (title === null) { + return; + } + + const newConfig = updateLovelaceContainer(this.lovelace!.config, path, { + ...section, + title: title || undefined, + }); + + this.lovelace!.saveConfig(newConfig); + } + + private async _deleteSection(ev) { + const index = ev.currentTarget.index; + + const path = [this.index!, index] as [number, number]; + + const section = findLovelaceContainer( + this.lovelace!.config, + path + ) as LovelaceRawSectionConfig; + + const title = section.title; + const cardCount = section.cards?.length; + + if (title || cardCount) { + const sectionName = title?.trim() + ? this.hass.localize( + "ui.panel.lovelace.editor.delete_section.named_section", + { name: title } + ) + : this.hass.localize( + "ui.panel.lovelace.editor.delete_section.unnamed_section" + ); + + const content = cardCount + ? this.hass.localize( + "ui.panel.lovelace.editor.delete_section.text_section_and_cards", + { + section: sectionName, + } + ) + : this.hass.localize( + "ui.panel.lovelace.editor.delete_section.text_section_only", + { + section: sectionName, + } + ); + + const confirm = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.lovelace.editor.delete_section.title" + ), + text: content, + confirmText: this.hass.localize("ui.common.delete"), + destructive: true, + }); + + if (!confirm) return; + } + + const newConfig = deleteSection(this.lovelace!.config, this.index!, index); + this.lovelace!.saveConfig(newConfig); + } + + private _sectionMoved(ev: CustomEvent) { + ev.stopPropagation(); + const { oldIndex, newIndex } = ev.detail; + + const newConfig = moveSection( + this.lovelace!.config, + [this.index!, oldIndex], + [this.index!, newIndex] + ); + this.lovelace!.saveConfig(newConfig); + } + + static get styles(): CSSResultGroup { + return css` + :host { + display: block; + } + + .section { + position: relative; + border-radius: var(--ha-card-border-radius, 12px); + } + + .container { + --column-count: 3; + display: grid; + grid-template-columns: repeat(var(--column-count), minmax(0, 1fr)); + gap: 8px 20px; + max-width: 1400px; + padding: 20px; + margin: 0 auto; + } + + @media (max-width: 1200px) { + .container { + --column-count: 2; + } + } + + @media (max-width: 600px) { + .container { + --column-count: 1; + padding: 8px; + } + } + + .section-actions { + position: absolute; + top: 0; + right: 0; + opacity: 1; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.2s ease-in-out; + background-color: rgba(var(--rgb-card-background-color), 0.3); + border-radius: 18px; + background: var(--secondary-background-color); + --mdc-icon-button-size: 36px; + --mdc-icon-size: 20px; + color: var(--primary-text-color); + } + + .handle { + cursor: grab; + padding: 8px; + } + + .add { + margin-top: calc(66px + 8px); + outline: none; + background: none; + cursor: pointer; + border-radius: var(--ha-card-border-radius, 12px); + border: 2px dashed var(--primary-color); + order: 1; + height: 66px; + padding: 8px; + box-sizing: content-box; + } + + .add:focus { + border: 2px solid var(--primary-color); + } + + .sortable-ghost { + border-radius: var(--ha-card-border-radius, 12px); + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-sections-view": SectionsView; + } +} diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 4338bc9eff..6d35d6a715 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -1,9 +1,17 @@ import { PropertyValues, ReactiveElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; +import { HASSDomEvent } from "../../../common/dom/fire_event"; import "../../../components/entity/ha-state-label-badge"; import "../../../components/ha-svg-icon"; import type { LovelaceViewElement } from "../../../data/lovelace"; +import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; +import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { LovelaceSectionConfig } from "../../../data/lovelace/config/section"; +import { + LovelaceViewConfig, + isStrategyView, +} from "../../../data/lovelace/config/view"; import type { HomeAssistant } from "../../../types"; import { createErrorBadgeConfig, @@ -20,25 +28,25 @@ import { import { createViewElement } from "../create-element/create-view-element"; import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog"; import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog"; -import { confDeleteCard } from "../editor/delete-card"; import { deleteCard } from "../editor/config-util"; +import { confDeleteCard } from "../editor/delete-card"; +import { + LovelaceCardPath, + parseLovelaceCardPath, +} from "../editor/lovelace-path"; +import { createErrorSectionConfig } from "../sections/hui-error-section"; +import "../sections/hui-section"; +import type { HuiSection } from "../sections/hui-section"; import { generateLovelaceViewStrategy } from "../strategies/get-strategy"; import type { Lovelace, LovelaceBadge, LovelaceCard } from "../types"; -import { PANEL_VIEW_LAYOUT, DEFAULT_VIEW_LAYOUT } from "./const"; -import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; -import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge"; -import { - LovelaceViewConfig, - isStrategyView, -} from "../../../data/lovelace/config/view"; -import { HASSDomEvent } from "../../../common/dom/fire_event"; +import { DEFAULT_VIEW_LAYOUT, PANEL_VIEW_LAYOUT } from "./const"; declare global { // for fire event interface HASSDomEvents { - "ll-create-card": undefined; - "ll-edit-card": { path: [number, number] }; - "ll-delete-card": { path: [number, number]; confirm: boolean }; + "ll-create-card": { suggested?: string[] } | undefined; + "ll-edit-card": { path: LovelaceCardPath }; + "ll-delete-card": { path: LovelaceCardPath; confirm: boolean }; } interface HTMLElementEventMap { "ll-create-card": HASSDomEvent; @@ -61,6 +69,8 @@ export class HUIView extends ReactiveElement { @state() private _badges: LovelaceBadge[] = []; + @state() private _sections: HuiSection[] = []; + private _layoutElementType?: string; private _layoutElement?: LovelaceViewElement; @@ -108,6 +118,27 @@ export class HUIView extends ReactiveElement { return element; } + // Public to make demo happy + public createSectionElement(sectionConfig: LovelaceSectionConfig) { + const element = document.createElement("hui-section"); + element.hass = this.hass; + element.lovelace = this.lovelace; + element.config = sectionConfig; + element.viewIndex = this.index; + element.addEventListener( + "ll-rebuild", + (ev: Event) => { + // In edit mode let it go to hui-root and rebuild whole view. + if (!this.lovelace!.editMode) { + ev.stopPropagation(); + this._rebuildSection(element, sectionConfig); + } + }, + { once: true } + ); + return element; + } + protected createRenderRoot() { return this; } @@ -139,7 +170,7 @@ export class HUIView extends ReactiveElement { } } - protected update(changedProperties) { + protected update(changedProperties: PropertyValues) { super.update(changedProperties); // If no layout element, we're still creating one @@ -162,6 +193,14 @@ export class HUIView extends ReactiveElement { } }); + this._sections.forEach((element) => { + try { + element.hass = this.hass; + } catch (e: any) { + this._rebuildSection(element, createErrorSectionConfig(e.message)); + } + }); + this._layoutElement.hass = this.hass; const oldHass = changedProperties.get("hass") as @@ -181,6 +220,14 @@ export class HUIView extends ReactiveElement { } if (changedProperties.has("lovelace")) { this._layoutElement.lovelace = this.lovelace; + this._sections.forEach((element) => { + try { + element.hass = this.hass; + element.lovelace = this.lovelace; + } catch (e: any) { + this._rebuildSection(element, createErrorSectionConfig(e.message)); + } + }); } if (changedProperties.has("_cards")) { this._layoutElement.cards = this._cards; @@ -220,6 +267,7 @@ export class HUIView extends ReactiveElement { this._createBadges(viewConfig); this._createCards(viewConfig); + this._createSections(viewConfig); this._layoutElement!.isStrategy = isStrategy; this._layoutElement!.hass = this.hass; this._layoutElement!.narrow = this.narrow; @@ -227,6 +275,7 @@ export class HUIView extends ReactiveElement { this._layoutElement!.index = this.index; this._layoutElement!.cards = this._cards; this._layoutElement!.badges = this._badges; + this._layoutElement!.sections = this._sections; applyThemesOnElement(this, this.hass.themes, viewConfig.theme); this._viewConfigTheme = viewConfig.theme; @@ -242,18 +291,21 @@ export class HUIView extends ReactiveElement { private _createLayoutElement(config: LovelaceViewConfig): void { this._layoutElement = createViewElement(config) as LovelaceViewElement; this._layoutElementType = config.type; - this._layoutElement.addEventListener("ll-create-card", () => { + this._layoutElement.addEventListener("ll-create-card", (ev) => { showCreateCardDialog(this, { lovelaceConfig: this.lovelace.config, saveConfig: this.lovelace.saveConfig, path: [this.index], + suggestedCards: ev.detail?.suggested, }); }); this._layoutElement.addEventListener("ll-edit-card", (ev) => { + const { cardIndex } = parseLovelaceCardPath(ev.detail.path); showEditCardDialog(this, { lovelaceConfig: this.lovelace.config, saveConfig: this.lovelace.saveConfig, - path: ev.detail.path, + path: [this.index], + cardIndex, }); }); this._layoutElement.addEventListener("ll-delete-card", (ev) => { @@ -303,6 +355,19 @@ export class HUIView extends ReactiveElement { }); } + private _createSections(config: LovelaceViewConfig): void { + if (!config || !config.sections || !Array.isArray(config.sections)) { + this._sections = []; + return; + } + + this._sections = config.sections.map((sectionConfig, index) => { + const element = this.createSectionElement(sectionConfig); + element.index = index; + return element; + }); + } + private _rebuildCard( cardElToReplace: LovelaceCard, config: LovelaceCardConfig @@ -343,6 +408,23 @@ export class HUIView extends ReactiveElement { curBadgeEl === badgeElToReplace ? newBadgeEl : curBadgeEl ); } + + private _rebuildSection( + sectionElToReplace: HuiSection, + config: LovelaceSectionConfig + ): void { + const newSectionEl = this.createSectionElement(config); + newSectionEl.index = sectionElToReplace.index; + if (sectionElToReplace.parentElement) { + sectionElToReplace.parentElement!.replaceChild( + newSectionEl, + sectionElToReplace + ); + } + this._sections = this._sections!.map((curSectionEl) => + curSectionEl === sectionElToReplace ? newSectionEl : curSectionEl + ); + } } declare global { diff --git a/src/translations/en.json b/src/translations/en.json index 1195ceb346..972c6753f5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5096,7 +5096,8 @@ "types": { "masonry": "Masonry (default)", "sidebar": "Sidebar", - "panel": "Panel (1 card)" + "panel": "Panel (1 card)", + "sections": "Sections (experimental)" }, "subview": "Subview", "subview_helper": "Subviews don't appear in tabs and have a back button.", @@ -5112,7 +5113,7 @@ "header": "Card configuration", "typed_header": "{type} Card configuration", "pick_card": "Which card would you like to add?", - "pick_card_view_title": "Which card would you like to add to your {name} view?", + "pick_card_title": "Which card would you like to add to {name}", "toggle_editor": "Toggle editor", "unsaved_changes": "You have unsaved changes", "confirm_cancel": "Are you sure you want to cancel?", @@ -5150,6 +5151,23 @@ "no_config": "No config found.", "no_views": "No views in this dashboard." }, + "section": { + "unnamed_section": "Unnamed section", + "add_card": "[%key:ui::panel::lovelace::editor::edit_card::add%]", + "add_section": "Add section" + }, + "delete_section": { + "title": "Delete section", + "named_section": "\"{name}\" section", + "unnamed_section": "This section", + "text_section_only": "{section} will be deleted.", + "text_section_and_cards": "{section} and all its cards will be deleted." + }, + "edit_section_title": { + "title": "Edit name", + "title_new": "Add name", + "input_label": "Name" + }, "suggest_card": { "header": "We created a suggestion for you", "create_own": "Pick different card", @@ -5455,7 +5473,10 @@ "state": "State", "secondary_info_attribute": "Secondary info attribute", "search": "Search", - "state_color": "Color icons based on state?" + "state_color": "Color icons based on state?", + "suggested_cards": "Suggested cards", + "other_cards": "Other cards", + "custom_cards": "Custom cards" }, "map": { "name": "Map", diff --git a/test/panels/lovelace/editor/config-util.spec.ts b/test/panels/lovelace/editor/config-util.spec.ts index a437b28cfa..b625636736 100644 --- a/test/panels/lovelace/editor/config-util.spec.ts +++ b/test/panels/lovelace/editor/config-util.spec.ts @@ -1,63 +1,12 @@ import { assert } from "chai"; +import { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; import { - swapCard, - moveCard, + moveCardToContainer, swapView, } from "../../../../src/panels/lovelace/editor/config-util"; -import { LovelaceConfig } from "../../../../src/data/lovelace/config/types"; -describe("swapCard", () => { - it("swaps 2 cards in same view", () => { - const config: LovelaceConfig = { - views: [ - {}, - { - cards: [{ type: "card1" }, { type: "card2" }], - }, - ], - }; - - const result = swapCard(config, [1, 0], [1, 1]); - const expected = { - views: [ - {}, - { - cards: [{ type: "card2" }, { type: "card1" }], - }, - ], - }; - assert.deepEqual(expected, result); - }); - - it("swaps 2 cards in different views", () => { - const config: LovelaceConfig = { - views: [ - { - cards: [{ type: "v1-c1" }, { type: "v1-c2" }], - }, - { - cards: [{ type: "v2-c1" }, { type: "v2-c2" }], - }, - ], - }; - - const result = swapCard(config, [0, 0], [1, 1]); - const expected: LovelaceConfig = { - views: [ - { - cards: [{ type: "v2-c2" }, { type: "v1-c2" }], - }, - { - cards: [{ type: "v2-c1" }, { type: "v1-c1" }], - }, - ], - }; - assert.deepEqual(expected, result); - }); -}); - -describe("moveCard", () => { +describe("moveCardToContainer", () => { it("move a card to an empty view", () => { const config: LovelaceConfig = { views: [ @@ -68,7 +17,7 @@ describe("moveCard", () => { ], }; - const result = moveCard(config, [1, 0], [0]); + const result = moveCardToContainer(config, [1, 0], [0]); const expected: LovelaceConfig = { views: [ { @@ -94,7 +43,7 @@ describe("moveCard", () => { ], }; - const result = moveCard(config, [1, 0], [0]); + const result = moveCardToContainer(config, [1, 0], [0]); const expected: LovelaceConfig = { views: [ { @@ -121,12 +70,12 @@ describe("moveCard", () => { }; const result = () => { - moveCard(config, [1, 0], [1]); + moveCardToContainer(config, [1, 0], [1]); }; assert.throws( result, Error, - "You cannot move a card to the view it is in." + "You cannot move a card to the view or section it is in." ); }); }); From 3e51f9a505a8ec352fbc3982043eed129d17e4a9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:14:06 -0500 Subject: [PATCH 067/116] Update typescript-eslint monorepo to v7.0.2 (#19862) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 104 +++++++++++++++++++++++++-------------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index ccb14c19d0..d2b7c161ec 100644 --- a/package.json +++ b/package.json @@ -183,8 +183,8 @@ "@types/tar": "6.1.11", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", - "@typescript-eslint/eslint-plugin": "7.0.1", - "@typescript-eslint/parser": "7.0.1", + "@typescript-eslint/eslint-plugin": "7.0.2", + "@typescript-eslint/parser": "7.0.2", "@web/dev-server": "0.1.38", "@web/dev-server-rollup": "0.4.1", "babel-loader": "9.1.3", diff --git a/yarn.lock b/yarn.lock index a2e1e658ac..b31a7efbdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4524,15 +4524,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/eslint-plugin@npm:7.0.1" +"@typescript-eslint/eslint-plugin@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/eslint-plugin@npm:7.0.2" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:7.0.1" - "@typescript-eslint/type-utils": "npm:7.0.1" - "@typescript-eslint/utils": "npm:7.0.1" - "@typescript-eslint/visitor-keys": "npm:7.0.1" + "@typescript-eslint/scope-manager": "npm:7.0.2" + "@typescript-eslint/type-utils": "npm:7.0.2" + "@typescript-eslint/utils": "npm:7.0.2" + "@typescript-eslint/visitor-keys": "npm:7.0.2" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -4545,44 +4545,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/0862e8ec8677fcea794394fc9eab8dba11043c08452722790e0d296d4ee84713180676e1e3135be4203ace7bb73933c94159255cb9190c7bc13bf7f03a361915 + checksum: 10/430b2f7ca36ee73dc75c1d677088709f3c9d5bbb4fffa3cfbe1b7d63979ee397f7a4a2a1386e05a04991500fa0ab0dd5272e8603a2b20f42e4bf590603500858 languageName: node linkType: hard -"@typescript-eslint/parser@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/parser@npm:7.0.1" +"@typescript-eslint/parser@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/parser@npm:7.0.2" dependencies: - "@typescript-eslint/scope-manager": "npm:7.0.1" - "@typescript-eslint/types": "npm:7.0.1" - "@typescript-eslint/typescript-estree": "npm:7.0.1" - "@typescript-eslint/visitor-keys": "npm:7.0.1" + "@typescript-eslint/scope-manager": "npm:7.0.2" + "@typescript-eslint/types": "npm:7.0.2" + "@typescript-eslint/typescript-estree": "npm:7.0.2" + "@typescript-eslint/visitor-keys": "npm:7.0.2" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/b4ba1743ab730268a1924139f072e4a0a56959526fb6377e1b3964518b6c6851733ae446a44d29fed1cb96669e2913cca524895ce77a6205aaed8bda00e8cd5d + checksum: 10/18d6e1bda64013f7d66164164c57a10390f7979db55b265062ae9337e11e0921bffca10870e252cd0bd198f79ffa2e87a652e57110e5b1b4cc738453154c205c languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/scope-manager@npm:7.0.1" +"@typescript-eslint/scope-manager@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/scope-manager@npm:7.0.2" dependencies: - "@typescript-eslint/types": "npm:7.0.1" - "@typescript-eslint/visitor-keys": "npm:7.0.1" - checksum: 10/dade6055bb853adb54de795cc3da5ab8550236d4186f108573fdb02e636ab7fc4300a55b506698ced4087ca43b143a5593931cb3195ab4790470b456d9ff8846 + "@typescript-eslint/types": "npm:7.0.2" + "@typescript-eslint/visitor-keys": "npm:7.0.2" + checksum: 10/773ea6e61f741777e69a469641f3db0d3c2301c0102667825fb235ed5a65c95f6d6b31b19e734b9a215acc0c7c576c65497635b8d5928eeddb58653ceb13d2d5 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/type-utils@npm:7.0.1" +"@typescript-eslint/type-utils@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/type-utils@npm:7.0.2" dependencies: - "@typescript-eslint/typescript-estree": "npm:7.0.1" - "@typescript-eslint/utils": "npm:7.0.1" + "@typescript-eslint/typescript-estree": "npm:7.0.2" + "@typescript-eslint/utils": "npm:7.0.2" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -4590,23 +4590,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/cf20a3c0e56121ac62467e48121e135798db6d2999bd4f96ed44edc39f2597812d12b1bd6a378adec54d6c5e7db75fa5f98a27ce399792a2c8a5bbd3649952f7 + checksum: 10/63bf19c9f5bbcb0f3e127f509d85dc49be4e5e51781d78f58c96786089e7c909b25d35d0248a6a758e2f7d5b5223d2262c2d597ab71f226af6beb499ae950645 languageName: node linkType: hard -"@typescript-eslint/types@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/types@npm:7.0.1" - checksum: 10/c08b2d34bab2a877a45a1e4c2923f50d03022b682b7aaba929ae2a9a5ad32db0e46265544a6616ccb98654b434250621be0e282fc5b21b8ccaf6b78741d68f67 +"@typescript-eslint/types@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/types@npm:7.0.2" + checksum: 10/2cba8a0355cc7357db142fa597d02cf39e1d1cb0ec87c80e91daaa2b87f2a794d2649def9d7b2aa435691c3810d2cbd4cdc21668b19b991863f0d54d4a22da82 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/typescript-estree@npm:7.0.1" +"@typescript-eslint/typescript-estree@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/typescript-estree@npm:7.0.2" dependencies: - "@typescript-eslint/types": "npm:7.0.1" - "@typescript-eslint/visitor-keys": "npm:7.0.1" + "@typescript-eslint/types": "npm:7.0.2" + "@typescript-eslint/visitor-keys": "npm:7.0.2" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -4616,34 +4616,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/b0b0adc84502d1ffcf3a0024179e0f2780be5f8b0a18328db46d430efc4e38a7965656b4392dd47d6176bbb1ee200aec6dd8581c39b606e260750574358cde9f + checksum: 10/307080e29c22fc69f0ce7ab7101e1629e05f45a9e541c250e03d06b61336ab0ccb5f0a7354ee3da4e38d5cade4dd2fb7bb396cd7cbe74c2c4b3e29706a70abcc languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/utils@npm:7.0.1" +"@typescript-eslint/utils@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/utils@npm:7.0.2" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:7.0.1" - "@typescript-eslint/types": "npm:7.0.1" - "@typescript-eslint/typescript-estree": "npm:7.0.1" + "@typescript-eslint/scope-manager": "npm:7.0.2" + "@typescript-eslint/types": "npm:7.0.2" + "@typescript-eslint/typescript-estree": "npm:7.0.2" semver: "npm:^7.5.4" peerDependencies: eslint: ^8.56.0 - checksum: 10/b7e0cb2994f73b3f416684dc175d4e1da5f8306d6c81abbad2f219fa3e4f29154063a3c9568e4a1f879a38b79c62250e596e4ed7265f7bd1ed9b3db806cb92b7 + checksum: 10/e68bac777419cd529371f7f29f534efaeca130c90ed9723bfc7aac451d61ca3fc4ebd310e2c015e29e8dc7be4734ae46258ca8755897d7f5e3bb502660d5372f languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.0.1": - version: 7.0.1 - resolution: "@typescript-eslint/visitor-keys@npm:7.0.1" +"@typescript-eslint/visitor-keys@npm:7.0.2": + version: 7.0.2 + resolution: "@typescript-eslint/visitor-keys@npm:7.0.2" dependencies: - "@typescript-eslint/types": "npm:7.0.1" + "@typescript-eslint/types": "npm:7.0.2" eslint-visitor-keys: "npm:^3.4.1" - checksum: 10/915c5b19302a4c76e843cd2d04a9a2b11907e658d7018c8b55c338b090d9115d3719809aa05b8af130cc1b216c77626d210c20f705b732e83d04ceae0c112f6b + checksum: 10/da6c1b0729af99216cde3a65d4e91584a81fc6c9dff7ba291089f01bf7262de375f58c4c4246e5fbc29f51258db7725d9c830f82ccbd1cda812fd13c51480cda languageName: node linkType: hard @@ -9603,8 +9603,8 @@ __metadata: "@types/tar": "npm:6.1.11" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@typescript-eslint/eslint-plugin": "npm:7.0.1" - "@typescript-eslint/parser": "npm:7.0.1" + "@typescript-eslint/eslint-plugin": "npm:7.0.2" + "@typescript-eslint/parser": "npm:7.0.2" "@vaadin/combo-box": "npm:24.3.6" "@vaadin/vaadin-themable-mixin": "npm:24.3.6" "@vibrant/color": "npm:3.2.1-alpha.1" From 93046d78f6c86efa298da5a07987b26b62430d5c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:15:30 -0500 Subject: [PATCH 068/116] Update dependency webpackbar to v6.0.1 (#19859) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index d2b7c161ec..6d76a4e9c6 100644 --- a/package.json +++ b/package.json @@ -245,7 +245,7 @@ "webpack-dev-server": "4.15.1", "webpack-manifest-plugin": "5.0.0", "webpack-stats-plugin": "1.1.3", - "webpackbar": "6.0.0", + "webpackbar": "6.0.1", "workbox-build": "7.0.0" }, "_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch", diff --git a/yarn.lock b/yarn.lock index b31a7efbdb..943145f708 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9715,7 +9715,7 @@ __metadata: webpack-dev-server: "npm:4.15.1" webpack-manifest-plugin: "npm:5.0.0" webpack-stats-plugin: "npm:1.1.3" - webpackbar: "npm:6.0.0" + webpackbar: "npm:6.0.1" weekstart: "npm:2.0.0" workbox-build: "npm:7.0.0" workbox-cacheable-response: "npm:7.0.0" @@ -14628,7 +14628,7 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.6.0": +"std-env@npm:^3.7.0": version: 3.7.0 resolution: "std-env@npm:3.7.0" checksum: 10/6ee0cca1add3fd84656b0002cfbc5bfa20340389d9ba4720569840f1caa34bce74322aef4c93f046391583e50649d0cf81a5f8fe1d411e50b659571690a45f12 @@ -16277,9 +16277,9 @@ __metadata: languageName: node linkType: hard -"webpackbar@npm:6.0.0": - version: 6.0.0 - resolution: "webpackbar@npm:6.0.0" +"webpackbar@npm:6.0.1": + version: 6.0.1 + resolution: "webpackbar@npm:6.0.1" dependencies: ansi-escapes: "npm:^4.3.2" chalk: "npm:^4.1.2" @@ -16287,11 +16287,11 @@ __metadata: figures: "npm:^3.2.0" markdown-table: "npm:^2.0.0" pretty-time: "npm:^1.1.0" - std-env: "npm:^3.6.0" + std-env: "npm:^3.7.0" wrap-ansi: "npm:^7.0.0" peerDependencies: webpack: 3 || 4 || 5 - checksum: 10/aa26c2dff6c2384f39b77e57375af52a65fbd3c54b9a5a8e1c53e79b0c25eaabc8602469ec3742bf93f7d178f5a96e3d6bd4fcbb256808e730eecb91091df466 + checksum: 10/9da47f8dcbc9173b19e41e3e1049fa451b0c02095ffa003e8c09c56aa2cc544334d1c6fff0797162a807b29090db9cf9a269cd5ec453196142543f9275cbbf70 languageName: node linkType: hard From 1869260868728a04236d2f79dd96d23c3ccf80c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:17:02 -0500 Subject: [PATCH 069/116] Update CodeMirror (#19857) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 ++-- yarn.lock | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 6d76a4e9c6..98f0b21bcf 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "@codemirror/language": "6.10.1", "@codemirror/legacy-modes": "6.3.3", "@codemirror/search": "6.5.6", - "@codemirror/state": "6.4.0", - "@codemirror/view": "6.24.0", + "@codemirror/state": "6.4.1", + "@codemirror/view": "6.24.1", "@egjs/hammerjs": "2.0.17", "@formatjs/intl-datetimeformat": "6.12.2", "@formatjs/intl-displaynames": "6.6.6", diff --git a/yarn.lock b/yarn.lock index 943145f708..2d5f024887 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1519,21 +1519,21 @@ __metadata: languageName: node linkType: hard -"@codemirror/state@npm:6.4.0, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.4.0": - version: 6.4.0 - resolution: "@codemirror/state@npm:6.4.0" - checksum: 10/d9129c456d1589ca376594620bad10c51d3dcdb57950f34637cea0e2ea073a695d426dc1cfc9b909b07365c236a6312da1eaf740c384c853009742493b8c9935 +"@codemirror/state@npm:6.4.1, @codemirror/state@npm:^6.0.0, @codemirror/state@npm:^6.4.0": + version: 6.4.1 + resolution: "@codemirror/state@npm:6.4.1" + checksum: 10/a9ec56c7d7d52034ce8ebea3a9a4d216b9e972d701b32b5000e56c97790d0d46af129aeba0b80bed36648b4024b3ba3e4910cf5bfed11de4a9e89252e0707a70 languageName: node linkType: hard -"@codemirror/view@npm:6.24.0, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0": - version: 6.24.0 - resolution: "@codemirror/view@npm:6.24.0" +"@codemirror/view@npm:6.24.1, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0": + version: 6.24.1 + resolution: "@codemirror/view@npm:6.24.1" dependencies: "@codemirror/state": "npm:^6.4.0" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/f8d4d4403d9483801cd4bab19abe6eba662d1c21f2726a54dbf9779770b9c034017fd28cf3c6cbf1230d1ec00370d5725dc4c9e81c55b9a534bfcf9fae264f4a + checksum: 10/66428da341241a865f75c88c4e2984234919ae46a76bba24822867166ba3d15a9df0235b120c491a5ffacdfed7f5d748cae083e19c48656ee9b93c40a5f3c7e1 languageName: node linkType: hard @@ -9519,8 +9519,8 @@ __metadata: "@codemirror/language": "npm:6.10.1" "@codemirror/legacy-modes": "npm:6.3.3" "@codemirror/search": "npm:6.5.6" - "@codemirror/state": "npm:6.4.0" - "@codemirror/view": "npm:6.24.0" + "@codemirror/state": "npm:6.4.1" + "@codemirror/view": "npm:6.24.1" "@egjs/hammerjs": "npm:2.0.17" "@formatjs/intl-datetimeformat": "npm:6.12.2" "@formatjs/intl-displaynames": "npm:6.6.6" From 766fd4cbf5c1de5f5b69dd7e2089f657a90b0f23 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:19:44 -0500 Subject: [PATCH 070/116] Update dependency webpack to v5.90.3 (#19858) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 98f0b21bcf..9c3c68c1ca 100644 --- a/package.json +++ b/package.json @@ -240,7 +240,7 @@ "typescript": "5.3.3", "vinyl-buffer": "1.0.1", "vinyl-source-stream": "2.0.0", - "webpack": "5.90.2", + "webpack": "5.90.3", "webpack-cli": "5.1.4", "webpack-dev-server": "4.15.1", "webpack-manifest-plugin": "5.0.0", diff --git a/yarn.lock b/yarn.lock index 2d5f024887..52be67b231 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9710,7 +9710,7 @@ __metadata: vis-network: "npm:9.1.9" vue: "npm:2.7.16" vue2-daterange-picker: "npm:0.6.8" - webpack: "npm:5.90.2" + webpack: "npm:5.90.3" webpack-cli: "npm:5.1.4" webpack-dev-server: "npm:4.15.1" webpack-manifest-plugin: "npm:5.0.0" @@ -16240,9 +16240,9 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.90.2": - version: 5.90.2 - resolution: "webpack@npm:5.90.2" +"webpack@npm:5.90.3": + version: 5.90.3 + resolution: "webpack@npm:5.90.3" dependencies: "@types/eslint-scope": "npm:^3.7.3" "@types/estree": "npm:^1.0.5" @@ -16273,7 +16273,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 10/4eaeed1255c9c7738921c4ce4facdb3b78dbfcb3441496942f6d160a41fbcebd24fb2c6dbb64739b357c5ff78e5a298f6c82eca482438b95130a3ba4e16d084a + checksum: 10/48c9696eca950bfa7c943a24b8235fdf0575acd73a8eb1661f8189d3d1f431362f3a0e158e2941a7e4f0852ea6e32d7d4e89283149247e4389a8aad0fe6c247e languageName: node linkType: hard From 0ff27154e63523d36e5f80ad7165f0967bd39b17 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 14:32:13 -0500 Subject: [PATCH 071/116] Update fullcalendar monorepo to v6.1.11 (#19865) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 12 ++++----- yarn.lock | 72 ++++++++++++++++++++++++++-------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index 9c3c68c1ca..30bd82121d 100644 --- a/package.json +++ b/package.json @@ -43,12 +43,12 @@ "@formatjs/intl-numberformat": "8.10.0", "@formatjs/intl-pluralrules": "5.2.12", "@formatjs/intl-relativetimeformat": "11.2.12", - "@fullcalendar/core": "6.1.10", - "@fullcalendar/daygrid": "6.1.10", - "@fullcalendar/interaction": "6.1.10", - "@fullcalendar/list": "6.1.10", - "@fullcalendar/luxon3": "6.1.10", - "@fullcalendar/timegrid": "6.1.10", + "@fullcalendar/core": "6.1.11", + "@fullcalendar/daygrid": "6.1.11", + "@fullcalendar/interaction": "6.1.11", + "@fullcalendar/list": "6.1.11", + "@fullcalendar/luxon3": "6.1.11", + "@fullcalendar/timegrid": "6.1.11", "@lezer/highlight": "1.2.0", "@lit-labs/context": "0.4.1", "@lit-labs/motion": "1.0.7", diff --git a/yarn.lock b/yarn.lock index 52be67b231..0e6215e337 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1749,60 +1749,60 @@ __metadata: languageName: node linkType: hard -"@fullcalendar/core@npm:6.1.10": - version: 6.1.10 - resolution: "@fullcalendar/core@npm:6.1.10" +"@fullcalendar/core@npm:6.1.11": + version: 6.1.11 + resolution: "@fullcalendar/core@npm:6.1.11" dependencies: preact: "npm:~10.12.1" - checksum: 10/0ca26aefd3e553dac9019838e46ab0e73f7df4954f4d2cbbda4ad73f4e8b9889e4a016cdcc7ae5f548d08d147d6315dffede51f8e213eff5ef63f2dcca2d759f + checksum: 10/7624841879d34fdf769c7610e95cef820ea52eb45c74d51be426bb2acc05cb57806785fc1b1a1282b156470c1f04c976314327fbff63e1b115b7108649aeb1c8 languageName: node linkType: hard -"@fullcalendar/daygrid@npm:6.1.10, @fullcalendar/daygrid@npm:~6.1.10": - version: 6.1.10 - resolution: "@fullcalendar/daygrid@npm:6.1.10" +"@fullcalendar/daygrid@npm:6.1.11, @fullcalendar/daygrid@npm:~6.1.11": + version: 6.1.11 + resolution: "@fullcalendar/daygrid@npm:6.1.11" peerDependencies: - "@fullcalendar/core": ~6.1.10 - checksum: 10/6c58038c1a5b6ba439f0b5d3fb9d53e7fabbf23039d18d6d60036acceee399cd1cbf6038d4b8973ee4c54544029fb940d29ff7d26472a332248e24a6e5969bf6 + "@fullcalendar/core": ~6.1.11 + checksum: 10/c6165c22405d0fee19b2e4ac54b35115c8bc7e176e3ef60f06b1d7af93d4da1fa571acbd169faa3ea4743c55f6374c6e7f7cc774026a1ae0b83d80db33c4b391 languageName: node linkType: hard -"@fullcalendar/interaction@npm:6.1.10": - version: 6.1.10 - resolution: "@fullcalendar/interaction@npm:6.1.10" +"@fullcalendar/interaction@npm:6.1.11": + version: 6.1.11 + resolution: "@fullcalendar/interaction@npm:6.1.11" peerDependencies: - "@fullcalendar/core": ~6.1.10 - checksum: 10/890f7809e4587dc8aa37292d29806a932214848ce1de42092e7290d62206ee785957dc77468bf6b741ca2948cc0e8563bb4bc2397d83d52c440bb2b054da06f2 + "@fullcalendar/core": ~6.1.11 + checksum: 10/6d669d59f20615b71d42d0d053af39d512e824381d575554e016c9221f3f98e17b7770341711322f8455a9c4729450479e2cf4bf026ff123540bf671c6f59f33 languageName: node linkType: hard -"@fullcalendar/list@npm:6.1.10": - version: 6.1.10 - resolution: "@fullcalendar/list@npm:6.1.10" +"@fullcalendar/list@npm:6.1.11": + version: 6.1.11 + resolution: "@fullcalendar/list@npm:6.1.11" peerDependencies: - "@fullcalendar/core": ~6.1.10 - checksum: 10/773830aa2b67c7e10e38b64b9ecd9819901067dd6fd7c78c1ec840830c07166ca1280edd57b746a635ed93cbdbb732a8a5f2cf1725e9ba4d668e30765f61f337 + "@fullcalendar/core": ~6.1.11 + checksum: 10/13bb92c7ca4c7808ec57c1ada55a724ff7e8928bb118e1d9bbaf20fab8c6fb2486101e37b9422dc88ef50342354d35c436bbfa161b5ff09379a11c317f26c725 languageName: node linkType: hard -"@fullcalendar/luxon3@npm:6.1.10": - version: 6.1.10 - resolution: "@fullcalendar/luxon3@npm:6.1.10" +"@fullcalendar/luxon3@npm:6.1.11": + version: 6.1.11 + resolution: "@fullcalendar/luxon3@npm:6.1.11" peerDependencies: - "@fullcalendar/core": ~6.1.10 + "@fullcalendar/core": ~6.1.11 luxon: ^3.0.0 - checksum: 10/575c225cddff677f30d835d84c6957f47ac81b8f2a395f3e0d1d11ea4da75b2479b42431b2a3e16284a1caf5589796633289970eb63143fe9aa3ef818b78213f + checksum: 10/fc302aad0d1b080aac800956921358f10682d8b62d49ff78db85fbc6ea15c8799729679d164d70ac71cee77e0744da55cdfc9b295dcb35e0d6bf673649fd805d languageName: node linkType: hard -"@fullcalendar/timegrid@npm:6.1.10": - version: 6.1.10 - resolution: "@fullcalendar/timegrid@npm:6.1.10" +"@fullcalendar/timegrid@npm:6.1.11": + version: 6.1.11 + resolution: "@fullcalendar/timegrid@npm:6.1.11" dependencies: - "@fullcalendar/daygrid": "npm:~6.1.10" + "@fullcalendar/daygrid": "npm:~6.1.11" peerDependencies: - "@fullcalendar/core": ~6.1.10 - checksum: 10/0351679754f110610a87e1a08b60df3833837f828ede0bb804c07632d765d5061bd03913a06384078e567092ae3071826aa82620b79b7e92fb55b4c878000bcb + "@fullcalendar/core": ~6.1.11 + checksum: 10/b2794502a0aa35c33405e35b05d367abab983d15af568a285123a673414bf545c298f7227b661290f99a2ef37e90013513cab3e9d4d0c15840212cd3b429c99d languageName: node linkType: hard @@ -9530,12 +9530,12 @@ __metadata: "@formatjs/intl-numberformat": "npm:8.10.0" "@formatjs/intl-pluralrules": "npm:5.2.12" "@formatjs/intl-relativetimeformat": "npm:11.2.12" - "@fullcalendar/core": "npm:6.1.10" - "@fullcalendar/daygrid": "npm:6.1.10" - "@fullcalendar/interaction": "npm:6.1.10" - "@fullcalendar/list": "npm:6.1.10" - "@fullcalendar/luxon3": "npm:6.1.10" - "@fullcalendar/timegrid": "npm:6.1.10" + "@fullcalendar/core": "npm:6.1.11" + "@fullcalendar/daygrid": "npm:6.1.11" + "@fullcalendar/interaction": "npm:6.1.11" + "@fullcalendar/list": "npm:6.1.11" + "@fullcalendar/luxon3": "npm:6.1.11" + "@fullcalendar/timegrid": "npm:6.1.11" "@koa/cors": "npm:5.0.0" "@lezer/highlight": "npm:1.2.0" "@lit-labs/context": "npm:0.4.1" From a39cf99024c68a824cd52c403cc14fdc7588f745 Mon Sep 17 00:00:00 2001 From: Yosi Levy <37745463+yosilevy@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:14:32 +0200 Subject: [PATCH 072/116] RTL updates (#19848) * RTL updates * Additional fixes * TODO fix --- src/components/ha-climate-state.ts | 1 + src/components/ha-metric.ts | 8 +++++++- src/components/ha-qr-scanner.ts | 2 ++ src/components/ha-textfield.ts | 3 +-- src/components/media-player/dialog-media-player-browse.ts | 1 - src/components/media-player/ha-media-player-browse.ts | 1 + .../lights/ha-more-info-light-favorite-colors.ts | 2 ++ .../more-info/components/lights/light-color-rgb-picker.ts | 2 ++ .../voice-command-dialog/ha-voice-command-dialog.ts | 2 ++ src/layouts/hass-tabs-subpage-data-table.ts | 2 ++ src/onboarding/onboarding-location.ts | 4 ++++ src/panels/config/areas/ha-config-area-page.ts | 7 +++++++ src/panels/config/cloud/account/cloud-remote-pref.ts | 2 ++ src/panels/config/hardware/dialog-hardware-available.ts | 2 ++ .../integrations/ha-config-integrations-dashboard.ts | 7 ++++++- .../config/integrations/ha-integration-action-card.ts | 2 ++ .../integration-panels/zha/zha-add-devices-page.ts | 2 ++ .../integration-panels/zha/zha-cluster-commands.ts | 2 ++ src/panels/config/storage/ha-config-section-storage.ts | 2 ++ .../voice-assistants/expose/expose-assistant-icon.ts | 2 ++ .../developer-tools/template/developer-tools-template.ts | 2 ++ src/panels/lovelace/cards/hui-media-control-card.ts | 2 ++ src/panels/lovelace/cards/hui-todo-list-card.ts | 2 ++ src/panels/lovelace/entity-rows/hui-number-entity-row.ts | 1 + src/panels/media-browser/ha-panel-media-browser.ts | 1 - 25 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts index ca5d3a00e9..c2f35979e8 100644 --- a/src/components/ha-climate-state.ts +++ b/src/components/ha-climate-state.ts @@ -156,6 +156,7 @@ class HaClimateState extends LitElement { .current { color: var(--secondary-text-color); + direction: var(--direction); } .state-label { diff --git a/src/components/ha-metric.ts b/src/components/ha-metric.ts index 4a33812929..9d0fd3431e 100644 --- a/src/components/ha-metric.ts +++ b/src/components/ha-metric.ts @@ -19,7 +19,9 @@ class HaMetric extends LitElement { ${this.heading}
    - ${roundedValue} % + +
    ${roundedValue} %
    +
    50, @@ -70,6 +72,10 @@ class HaMetric extends LitElement { padding-inline-start: initial; flex-shrink: 0; } + .value > div { + direction: ltr; + text-align: var(--float-start); + } `; } } diff --git a/src/components/ha-qr-scanner.ts b/src/components/ha-qr-scanner.ts index 9d437164cc..b65f9ba07a 100644 --- a/src/components/ha-qr-scanner.ts +++ b/src/components/ha-qr-scanner.ts @@ -186,6 +186,8 @@ class HaQrScanner extends LitElement { position: absolute; bottom: 8px; right: 8px; + inset-inline-end: 8px; + inset-inline-start: initial; background: #727272b2; color: white; border-radius: 50%; diff --git a/src/components/ha-textfield.ts b/src/components/ha-textfield.ts index 170c0c179d..175b8ae290 100644 --- a/src/components/ha-textfield.ts +++ b/src/components/ha-textfield.ts @@ -90,7 +90,7 @@ export class HaTextField extends TextFieldBase { padding-right: var(--text-field-suffix-padding-right, 0px); padding-inline-start: var(--text-field-suffix-padding-left, 12px); padding-inline-end: var(--text-field-suffix-padding-right, 0px); - direction: var(--direction); + direction: ltr; } .mdc-text-field--with-leading-icon { padding-inline-start: var(--text-field-suffix-padding-left, 0px); @@ -199,7 +199,6 @@ export class HaTextField extends TextFieldBase { // safari workaround - must be explicit mainWindow.document.dir === "rtl" ? css` - .mdc-text-field__affix--suffix, .mdc-text-field--with-leading-icon, .mdc-text-field__icon--leading, .mdc-floating-label, diff --git a/src/components/media-player/dialog-media-player-browse.ts b/src/components/media-player/dialog-media-player-browse.ts index fd8bdba673..e0a66ae79c 100644 --- a/src/components/media-player/dialog-media-player-browse.ts +++ b/src/components/media-player/dialog-media-player-browse.ts @@ -223,7 +223,6 @@ class DialogMediaPlayerBrowse extends LitElement { ha-media-player-browse { --media-browser-max-height: calc(100vh - 65px); - direction: ltr; } :host(.opened) ha-media-player-browse { diff --git a/src/components/media-player/ha-media-player-browse.ts b/src/components/media-player/ha-media-player-browse.ts index e46c9e908c..6f205de5fd 100644 --- a/src/components/media-player/ha-media-player-browse.ts +++ b/src/components/media-player/ha-media-player-browse.ts @@ -879,6 +879,7 @@ export class HaMediaPlayerBrowse extends LitElement { display: flex; flex-direction: column; position: relative; + direction: ltr; } ha-circular-progress { diff --git a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts index 93a6b1528f..aa9893d000 100644 --- a/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts +++ b/src/dialogs/more-info/components/lights/ha-more-info-light-favorite-colors.ts @@ -322,6 +322,8 @@ export class HaMoreInfoLightFavoriteColors extends LitElement { position: absolute; top: -6px; right: -6px; + inset-inline-end: -6px; + inset-inline-start: initial; width: 20px; height: 20px; outline: none; diff --git a/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts index de42e5b054..a38cbe2b2e 100644 --- a/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts +++ b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts @@ -446,6 +446,8 @@ class LightRgbColorPicker extends LitElement { position: absolute; top: 0; right: 0; + inset-inline-end: 0; + inset-inline-start: initial; z-index: 1; } diff --git a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts index 700c6e8ea9..086e8b448d 100644 --- a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts @@ -647,6 +647,8 @@ export class HaVoiceCommandDialog extends LitElement { position: absolute; --mdc-icon-size: 16px; right: 5px; + inset-inline-end: 5px; + inset-inline-start: initial; top: 0px; } diff --git a/src/layouts/hass-tabs-subpage-data-table.ts b/src/layouts/hass-tabs-subpage-data-table.ts index 7af35ded29..096ec15e30 100644 --- a/src/layouts/hass-tabs-subpage-data-table.ts +++ b/src/layouts/hass-tabs-subpage-data-table.ts @@ -377,6 +377,8 @@ export class HaTabsSubpageDataTable extends LitElement { color: var(--text-primary-color); position: absolute; right: 0; + inset-inline-end: 0; + inset-inline-start: initial; top: 4px; font-size: 0.65em; } diff --git a/src/onboarding/onboarding-location.ts b/src/onboarding/onboarding-location.ts index 1d031b81f3..3027a93f9e 100644 --- a/src/onboarding/onboarding-location.ts +++ b/src/onboarding/onboarding-location.ts @@ -499,6 +499,8 @@ class OnboardingLocation extends LitElement { position: absolute; top: 10px; right: 10px; + inset-inline-end: 10px; + inset-inline-start: initial; --mdc-icon-button-size: 36px; --mdc-icon-size: 20px; color: var(--secondary-text-color); @@ -509,6 +511,8 @@ class OnboardingLocation extends LitElement { ha-textfield > ha-circular-progress { position: relative; left: 12px; + inset-inline-start: 12px; + inset-inline-end: initial; } ha-locations-editor { display: block; diff --git a/src/panels/config/areas/ha-config-area-page.ts b/src/panels/config/areas/ha-config-area-page.ts index 62abd48d4e..18ce4dd5b9 100644 --- a/src/panels/config/areas/ha-config-area-page.ts +++ b/src/panels/config/areas/ha-config-area-page.ts @@ -710,6 +710,8 @@ class HaConfigAreaPage extends LitElement { position: absolute; top: 4px; right: 4px; + inset-inline-end: 4px; + inset-inline-start: initial; display: none; } .img-container:hover .img-edit-btn { @@ -736,6 +738,11 @@ class HaConfigAreaPage extends LitElement { padding: 16px; color: var(--secondary-text-color); } + + mwc-button > ha-svg-icon { + margin-inline-start: 0; + margin-inline-end: 8px; + } `, ]; } diff --git a/src/panels/config/cloud/account/cloud-remote-pref.ts b/src/panels/config/cloud/account/cloud-remote-pref.ts index b74e94dce3..e94e50b56b 100644 --- a/src/panels/config/cloud/account/cloud-remote-pref.ts +++ b/src/panels/config/cloud/account/cloud-remote-pref.ts @@ -226,6 +226,8 @@ export class CloudRemotePref extends LitElement { position: absolute; right: 24px; top: 24px; + inset-inline-end: 24px; + inset-inline-start: initial; } .card-actions { display: flex; diff --git a/src/panels/config/hardware/dialog-hardware-available.ts b/src/panels/config/hardware/dialog-hardware-available.ts index b11d34410b..5cc858b1c8 100644 --- a/src/panels/config/hardware/dialog-hardware-available.ts +++ b/src/panels/config/hardware/dialog-hardware-available.ts @@ -172,6 +172,8 @@ class DialogHardwareAvailable extends LitElement implements HassDialog { position: absolute; right: 16px; top: 10px; + inset-inline-end: 16px; + inset-inline-start: initial; text-decoration: none; color: var(--primary-text-color); } diff --git a/src/panels/config/integrations/ha-config-integrations-dashboard.ts b/src/panels/config/integrations/ha-config-integrations-dashboard.ts index 8e0ed86b45..7a960576e8 100644 --- a/src/panels/config/integrations/ha-config-integrations-dashboard.ts +++ b/src/panels/config/integrations/ha-config-integrations-dashboard.ts @@ -877,6 +877,8 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { color: var(--text-primary-color); position: absolute; right: 0px; + inset-inline-end: 0px; + inset-inline-start: initial; top: 4px; font-size: 0.65em; } @@ -884,7 +886,10 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) { position: relative; } h1 { - margin: 8px 0 0 16px; + margin-top: 8px; + margin-left: 16px; + margin-inline-start: 16px; + margin-inline-end: initial; } 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 189b5d4b46..ada2e3b76b 100644 --- a/src/panels/config/integrations/ha-integration-action-card.ts +++ b/src/panels/config/integrations/ha-integration-action-card.ts @@ -90,6 +90,8 @@ export class HaIntegrationActionCard extends LitElement { position: absolute; top: 8px; right: 8px; + inset-inline-end: 8px; + inset-inline-start: initial; } .filler { flex: 1; diff --git a/src/panels/config/integrations/integration-panels/zha/zha-add-devices-page.ts b/src/panels/config/integrations/integration-panels/zha/zha-add-devices-page.ts index 11d37b1988..a7570e0a46 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-add-devices-page.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-add-devices-page.ts @@ -269,6 +269,8 @@ class ZHAAddDevicesPage extends LitElement { margin-inline-start: initial; top: -6px; right: 0; + inset-inline-end: 0; + inset-inline-start: initial; color: var(--primary-color); } .search-button { diff --git a/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts b/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts index 4872fde8e1..d6932b10ff 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-cluster-commands.ts @@ -241,6 +241,8 @@ export class ZHAClusterCommands extends LitElement { float: right; top: -6px; right: 0; + inset-inline-end: 0; + inset-inline-start: initial; padding-right: 0px; padding-inline-end: 0px; padding-inline-start: initial; diff --git a/src/panels/config/storage/ha-config-section-storage.ts b/src/panels/config/storage/ha-config-section-storage.ts index 708f79e861..89b5fc7cf0 100644 --- a/src/panels/config/storage/ha-config-section-storage.ts +++ b/src/panels/config/storage/ha-config-section-storage.ts @@ -334,6 +334,8 @@ class HaConfigSectionStorage extends LitElement { position: relative; top: -10px; right: 10px; + inset-inline-end: 10px; + inset-inline-start: initial; } .no-mounts { diff --git a/src/panels/config/voice-assistants/expose/expose-assistant-icon.ts b/src/panels/config/voice-assistants/expose/expose-assistant-icon.ts index 46d2291aa6..69d0bcf821 100644 --- a/src/panels/config/voice-assistants/expose/expose-assistant-icon.ts +++ b/src/panels/config/voice-assistants/expose/expose-assistant-icon.ts @@ -93,6 +93,8 @@ export class VoiceAssistantExposeAssistantIcon extends LitElement { --mdc-icon-size: 16px; right: 10px; top: -7px; + inset-inline-end: 10px; + inset-inline-start: initial; } `; } diff --git a/src/panels/developer-tools/template/developer-tools-template.ts b/src/panels/developer-tools/template/developer-tools-template.ts index 7732ad7760..e4162d3077 100644 --- a/src/panels/developer-tools/template/developer-tools-template.ts +++ b/src/panels/developer-tools/template/developer-tools-template.ts @@ -290,6 +290,8 @@ class HaPanelDevTemplate extends LitElement { position: absolute; top: 8px; right: 8px; + inset-inline-end: 8px; + inset-inline-start: initial; } ha-alert { diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts index 7d5a1d8cc3..d28d0f612f 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.ts +++ b/src/panels/lovelace/cards/hui-media-control-card.ts @@ -726,6 +726,8 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { position: absolute; right: 4px; --mdc-icon-size: 24px; + inset-inline-end: 4px; + inset-inline-start: initial; } .top-info { diff --git a/src/panels/lovelace/cards/hui-todo-list-card.ts b/src/panels/lovelace/cards/hui-todo-list-card.ts index 5094ea3d73..bed42344a7 100644 --- a/src/panels/lovelace/cards/hui-todo-list-card.ts +++ b/src/panels/lovelace/cards/hui-todo-list-card.ts @@ -732,6 +732,8 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard { .deleteItemButton { position: relative; left: 8px; + inset-inline-start: 8px; + inset-inline-end: initial; } ha-textfield { diff --git a/src/panels/lovelace/entity-rows/hui-number-entity-row.ts b/src/panels/lovelace/entity-rows/hui-number-entity-row.ts index 13afe9d3c5..1b57c6ee41 100644 --- a/src/panels/lovelace/entity-rows/hui-number-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-number-entity-row.ts @@ -139,6 +139,7 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow { } ha-textfield { text-align: end; + direction: ltr !important; } ha-slider { width: 100%; diff --git a/src/panels/media-browser/ha-panel-media-browser.ts b/src/panels/media-browser/ha-panel-media-browser.ts index 8713bed2a4..c5bc566f07 100644 --- a/src/panels/media-browser/ha-panel-media-browser.ts +++ b/src/panels/media-browser/ha-panel-media-browser.ts @@ -343,7 +343,6 @@ class PanelMediaBrowser extends LitElement { ha-media-player-browse { height: calc(100vh - (100px + var(--header-height))); - direction: ltr; } :host([narrow]) ha-media-player-browse { From e8fca5d93c5d34dbc614a04d9009ce2787881153 Mon Sep 17 00:00:00 2001 From: chammp <57918757+chammp@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:21:02 +0100 Subject: [PATCH 073/116] Directly prompt for lock code in dashboards (#19835) Prompt for lock codes in more places --- src/panels/lovelace/entity-rows/hui-lock-entity-row.ts | 10 ++++++---- src/state-summary/state-card-lock.ts | 10 +++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts b/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts index 6c757e22d7..128a80808a 100644 --- a/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-lock-entity-row.ts @@ -14,6 +14,7 @@ import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { EntityConfig, LovelaceRow } from "./types"; +import { callProtectedLockService } from "../../../data/lock"; @customElement("hui-lock-entity-row") class HuiLockEntityRow extends LitElement implements LovelaceRow { @@ -75,10 +76,11 @@ class HuiLockEntityRow extends LitElement implements LovelaceRow { private _callService(ev): void { ev.stopPropagation(); const stateObj = this.hass!.states[this._config!.entity]; - this.hass!.callService( - "lock", - stateObj.state === "locked" ? "unlock" : "lock", - { entity_id: stateObj.entity_id } + callProtectedLockService( + this, + this.hass!, + stateObj, + stateObj.state === "locked" ? "unlock" : "lock" ); } } diff --git a/src/state-summary/state-card-lock.ts b/src/state-summary/state-card-lock.ts index c35c4e3007..117a7c992b 100644 --- a/src/state-summary/state-card-lock.ts +++ b/src/state-summary/state-card-lock.ts @@ -11,7 +11,7 @@ import { import { customElement, property } from "lit/decorators"; import { supportsFeature } from "../common/entity/supports-feature"; import "../components/entity/state-info"; -import { LockEntityFeature } from "../data/lock"; +import { callProtectedLockService, LockEntityFeature } from "../data/lock"; import { HomeAssistant } from "../types"; import { haStyle } from "../resources/styles"; @@ -56,10 +56,10 @@ class StateCardLock extends LitElement { private async _callService(ev) { ev.stopPropagation(); const service = ev.target.dataset.service; - const data = { - entity_id: this.stateObj.entity_id, - }; - await this.hass.callService("lock", service, data); + if (!this.hass || !this.stateObj) { + return; + } + await callProtectedLockService(this, this.hass, this.stateObj, service); } static get styles(): CSSResultGroup { From 3a8e2c429fb5ef4dfd11b8c26303ccbf29c79b87 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:38:40 -0500 Subject: [PATCH 074/116] Update dependency eslint to v8.57.0 (#19882) --- package.json | 2 +- yarn.lock | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 30bd82121d..8c8f0f103c 100644 --- a/package.json +++ b/package.json @@ -191,7 +191,7 @@ "babel-plugin-template-html-minifier": "4.1.0", "chai": "5.1.0", "del": "7.1.0", - "eslint": "8.56.0", + "eslint": "8.57.0", "eslint-config-airbnb-base": "15.0.0", "eslint-config-airbnb-typescript": "17.1.0", "eslint-config-prettier": "9.1.0", diff --git a/yarn.lock b/yarn.lock index 0e6215e337..b085269924 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1597,10 +1597,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.56.0": - version: 8.56.0 - resolution: "@eslint/js@npm:8.56.0" - checksum: 10/97a4b5ccf7e24f4d205a1fb0f21cdcd610348ecf685f6798a48dd41ba443f2c1eedd3050ff5a0b8f30b8cf6501ab512aa9b76e531db15e59c9ebaa41f3162e37 +"@eslint/js@npm:8.57.0": + version: 8.57.0 + resolution: "@eslint/js@npm:8.57.0" + checksum: 10/3c501ce8a997cf6cbbaf4ed358af5492875e3550c19b9621413b82caa9ae5382c584b0efa79835639e6e0ddaa568caf3499318e5bdab68643ef4199dce5eb0a0 languageName: node linkType: hard @@ -1815,7 +1815,7 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.13": +"@humanwhocodes/config-array@npm:^0.11.14": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: @@ -8080,15 +8080,15 @@ __metadata: languageName: node linkType: hard -"eslint@npm:8.56.0": - version: 8.56.0 - resolution: "eslint@npm:8.56.0" +"eslint@npm:8.57.0": + version: 8.57.0 + resolution: "eslint@npm:8.57.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.6.1" "@eslint/eslintrc": "npm:^2.1.4" - "@eslint/js": "npm:8.56.0" - "@humanwhocodes/config-array": "npm:^0.11.13" + "@eslint/js": "npm:8.57.0" + "@humanwhocodes/config-array": "npm:^0.11.14" "@humanwhocodes/module-importer": "npm:^1.0.1" "@nodelib/fs.walk": "npm:^1.2.8" "@ungap/structured-clone": "npm:^1.2.0" @@ -8124,7 +8124,7 @@ __metadata: text-table: "npm:^0.2.0" bin: eslint: bin/eslint.js - checksum: 10/ef6193c6e4cef20774b985a5cc2fd4bf6d3c4decd423117cbc4a0196617861745db291217ad3c537bc3a160650cca965bc818f55e1f3e446af1fcb293f9940a5 + checksum: 10/00496e218b23747a7a9817bf58b522276d0dc1f2e546dceb4eea49f9871574088f72f1f069a6b560ef537efa3a75261b8ef70e51ef19033da1cc4c86a755ef15 languageName: node linkType: hard @@ -9629,7 +9629,7 @@ __metadata: deep-freeze: "npm:0.0.1" del: "npm:7.1.0" element-internals-polyfill: "npm:1.3.10" - eslint: "npm:8.56.0" + eslint: "npm:8.57.0" eslint-config-airbnb-base: "npm:15.0.0" eslint-config-airbnb-typescript: "npm:17.1.0" eslint-config-prettier: "npm:9.1.0" From bf56f50e0a6fbb13321ca4d6725c918b20e071a7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 11:26:11 +0100 Subject: [PATCH 075/116] Update dependency gulp-json-transform to v0.5.0 (#19885) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 27 +++++---------------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 8c8f0f103c..207dca4cb6 100644 --- a/package.json +++ b/package.json @@ -207,7 +207,7 @@ "glob": "10.3.10", "gulp": "4.0.2", "gulp-flatmap": "1.0.2", - "gulp-json-transform": "0.4.8", + "gulp-json-transform": "0.5.0", "gulp-merge-json": "2.1.2", "gulp-rename": "2.0.0", "gulp-zopfli-green": "6.0.1", diff --git a/yarn.lock b/yarn.lock index b085269924..fb0f4f42d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5854,13 +5854,6 @@ __metadata: languageName: node linkType: hard -"asap@npm:~2.0.6": - version: 2.0.6 - resolution: "asap@npm:2.0.6" - checksum: 10/b244c0458c571945e4b3be0b14eb001bea5596f9868cc50cc711dc03d58a7e953517d3f0dad81ccde3ff37d1f074701fa76a6f07d41aaa992d7204a37b915dda - languageName: node - linkType: hard - "assertion-error@npm:^2.0.1": version: 2.0.1 resolution: "assertion-error@npm:2.0.1" @@ -9305,17 +9298,16 @@ __metadata: languageName: node linkType: hard -"gulp-json-transform@npm:0.4.8": - version: 0.4.8 - resolution: "gulp-json-transform@npm:0.4.8" +"gulp-json-transform@npm:0.5.0": + version: 0.5.0 + resolution: "gulp-json-transform@npm:0.5.0" dependencies: ansi-colors: "npm:^1.0.1" fancy-log: "npm:^1.3.2" plugin-error: "npm:^1.0.1" - promise: "npm:^8.0.1" through2: "npm:^2.0.3" vinyl: "npm:^2.1.0" - checksum: 10/5b117e59cd806e530cb4f67089e28eb1a6234bfb1461497e540be2a24cd94c5a8ce573d5ff9222c1481d951316054742a2212c044df0e047256ce10ce7bcd328 + checksum: 10/0879149337450e4ada1363fa72c351d2386b249df31aa8313c699c267cae3ece14ff1ab77dc7bb6a67827902db68ee0e0232d67ebfca55900dc17f3cfe219c42 languageName: node linkType: hard @@ -9647,7 +9639,7 @@ __metadata: google-timezones-json: "npm:1.2.0" gulp: "npm:4.0.2" gulp-flatmap: "npm:1.0.2" - gulp-json-transform: "npm:0.4.8" + gulp-json-transform: "npm:0.5.0" gulp-merge-json: "npm:2.1.2" gulp-rename: "npm:2.0.0" gulp-zopfli-green: "npm:6.0.1" @@ -13193,15 +13185,6 @@ __metadata: languageName: node linkType: hard -"promise@npm:^8.0.1": - version: 8.3.0 - resolution: "promise@npm:8.3.0" - dependencies: - asap: "npm:~2.0.6" - checksum: 10/55e9d0d723c66810966bc055c6c77a3658c0af7e4a8cc88ea47aeaf2949ca0bd1de327d9c631df61236f5406ad478384fa19a77afb3f88c0303eba9e5eb0a8d8 - languageName: node - linkType: hard - "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" From bf8a33e086f54ade28c4f8d8a0e14ca09ad54d18 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 27 Feb 2024 12:50:37 +0100 Subject: [PATCH 076/116] Fix button card and sensor card in grid section (#19887) --- src/panels/lovelace/cards/hui-button-card.ts | 4 ++-- src/panels/lovelace/cards/hui-sensor-card.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 129390fe5b..2d440a3036 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -149,8 +149,8 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { public getGridSize(): [number, number] { if ( - (this._config?.show_icon && this._config?.show_name) || - this._config?.show_state + this._config?.show_icon && + (this._config?.show_name || this._config?.show_state) ) { return [2, 2]; } diff --git a/src/panels/lovelace/cards/hui-sensor-card.ts b/src/panels/lovelace/cards/hui-sensor-card.ts index d90858862d..709a6591d9 100644 --- a/src/panels/lovelace/cards/hui-sensor-card.ts +++ b/src/panels/lovelace/cards/hui-sensor-card.ts @@ -72,7 +72,7 @@ class HuiSensorCard extends HuiEntityCard { super.setConfig(entityCardConfig); } - public getSize(): [number, number] { + public getGridSize(): [number, number] { return [2, 2]; } From bb25817baeafa11f926c4a981131943f3ebfeb86 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 27 Feb 2024 12:50:59 +0100 Subject: [PATCH 077/116] Reduce drag and drop delay on touch (#19888) --- src/panels/lovelace/sections/hui-grid-section.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/lovelace/sections/hui-grid-section.ts b/src/panels/lovelace/sections/hui-grid-section.ts index 1a6ed30ff7..b63e9fac8f 100644 --- a/src/panels/lovelace/sections/hui-grid-section.ts +++ b/src/panels/lovelace/sections/hui-grid-section.ts @@ -17,7 +17,7 @@ import { moveCard } from "../editor/config-util"; import type { Lovelace, LovelaceCard } from "../types"; const CARD_SORTABLE_OPTIONS: HaSortableOptions = { - delay: 200, + delay: 100, delayOnTouchOnly: true, direction: "vertical", invertedSwapThreshold: 0.7, From b9935717dc6da595ae7190bd2b1b886ab3c597c4 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 27 Feb 2024 12:51:35 +0100 Subject: [PATCH 078/116] Focus cancel button on destructive confirmation dialog (#19889) --- src/dialogs/generic/dialog-box.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/dialogs/generic/dialog-box.ts b/src/dialogs/generic/dialog-box.ts index 2660b56799..216b79b637 100644 --- a/src/dialogs/generic/dialog-box.ts +++ b/src/dialogs/generic/dialog-box.ts @@ -89,7 +89,12 @@ class DialogBox extends LitElement {
    ${confirmPrompt && html` - + ${this._params.dismissText ? this._params.dismissText : this.hass.localize("ui.dialogs.generic.cancel")} @@ -97,7 +102,8 @@ class DialogBox extends LitElement { `} Date: Tue, 27 Feb 2024 07:17:29 -0500 Subject: [PATCH 079/116] Resolve iframe CORS errors - Fix #19724 (#19884) * Resolve iframe CORS errors - Fix #19724 * add newline --- src/common/dom/get_main_window.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/common/dom/get_main_window.ts b/src/common/dom/get_main_window.ts index a2a5cca7b1..0f727eb244 100644 --- a/src/common/dom/get_main_window.ts +++ b/src/common/dom/get_main_window.ts @@ -1,8 +1,13 @@ import { MAIN_WINDOW_NAME } from "../../data/main_window"; -export const mainWindow = - window.name === MAIN_WINDOW_NAME - ? window - : parent.name === MAIN_WINDOW_NAME - ? parent - : top!; +export const mainWindow = (() => { + try { + return window.name === MAIN_WINDOW_NAME + ? window + : parent.name === MAIN_WINDOW_NAME + ? parent + : top!; + } catch { + return window; + } +})(); From 8fe7711634c09a01ee02990d2050c0c19299f711 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 08:05:46 -0500 Subject: [PATCH 080/116] Bump es5-ext from 0.10.62 to 0.10.63 (#19883) Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.62 to 0.10.63. - [Release notes](https://github.com/medikoo/es5-ext/releases) - [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md) - [Commits](https://github.com/medikoo/es5-ext/compare/v0.10.62...v0.10.63) --- updated-dependencies: - dependency-name: es5-ext dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index fb0f4f42d5..d7d6e089e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7769,14 +7769,15 @@ __metadata: languageName: node linkType: hard -"es5-ext@npm:^0.10.35, es5-ext@npm:^0.10.46, es5-ext@npm:^0.10.50": - version: 0.10.62 - resolution: "es5-ext@npm:0.10.62" +"es5-ext@npm:^0.10.35, es5-ext@npm:^0.10.46, es5-ext@npm:^0.10.50, es5-ext@npm:^0.10.62, es5-ext@npm:~0.10.14": + version: 0.10.63 + resolution: "es5-ext@npm:0.10.63" dependencies: es6-iterator: "npm:^2.0.3" es6-symbol: "npm:^3.1.3" + esniff: "npm:^2.0.1" next-tick: "npm:^1.1.0" - checksum: 10/3f6a3bcdb7ff82aaf65265799729828023c687a2645da04005b8f1dc6676a0c41fd06571b2517f89dcf143e0268d3d9ef0fdfd536ab74580083204c688d6fb45 + checksum: 10/69e373ffcf4664f12697adfab4a043a5cd93386c5e9d9b3eda4bd59f3591c7bebba3647475a3c9ccb1b48ec941b93c9507bf54727f0311a65e7220bc895eade9 languageName: node linkType: hard @@ -8121,6 +8122,18 @@ __metadata: languageName: node linkType: hard +"esniff@npm:^2.0.1": + version: 2.0.1 + resolution: "esniff@npm:2.0.1" + dependencies: + d: "npm:^1.0.1" + es5-ext: "npm:^0.10.62" + event-emitter: "npm:^0.3.5" + type: "npm:^2.7.2" + checksum: 10/f6a2abd2f8c5fe57c5fcf53e5407c278023313d0f6c3a92688e7122ab9ac233029fd424508a196ae5bc561aa1f67d23f4e2435b1a0d378030f476596129056ac + languageName: node + linkType: hard + "espree@npm:^9.6.0, espree@npm:^9.6.1": version: 9.6.1 resolution: "espree@npm:9.6.1" @@ -8199,6 +8212,16 @@ __metadata: languageName: node linkType: hard +"event-emitter@npm:^0.3.5": + version: 0.3.5 + resolution: "event-emitter@npm:0.3.5" + dependencies: + d: "npm:1" + es5-ext: "npm:~0.10.14" + checksum: 10/a7f5ea80029193f4869782d34ef7eb43baa49cd397013add1953491b24588468efbe7e3cc9eb87d53f33397e7aab690fd74c079ec440bf8b12856f6bdb6e9396 + languageName: node + linkType: hard + "eventemitter3@npm:^4.0.0": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" From d6d61a4137cc91998d3883d2d8348d0bd030bbcd Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Feb 2024 09:05:28 -0500 Subject: [PATCH 081/116] Show script fields in Script more info dialog (#19879) * Show script fields in more info dialog * Apply suggestions from code review Co-authored-by: Bram Kragten * Guard for state obj changes * Update src/components/ha-service-control.ts Co-authored-by: Marc Geurts --------- Co-authored-by: Bram Kragten Co-authored-by: Marc Geurts --- src/components/ha-service-control.ts | 58 +++++---- src/dialogs/more-info/const.ts | 2 +- .../more-info/controls/more-info-script.ts | 110 +++++++++++++++++- .../entity-rows/hui-script-entity-row.ts | 12 +- src/state-summary/state-card-script.ts | 13 ++- 5 files changed, 166 insertions(+), 29 deletions(-) diff --git a/src/components/ha-service-control.ts b/src/components/ha-service-control.ts index 57c3e2b3ba..778216f792 100644 --- a/src/components/ha-service-control.ts +++ b/src/components/ha-service-control.ts @@ -93,6 +93,8 @@ export class HaServiceControl extends LitElement { @property({ type: Boolean, reflect: true }) public hidePicker = false; + @property({ type: Boolean }) public hideDescription = false; + @state() private _value!: this["value"]; @state() private _checkedKeys = new Set(); @@ -373,7 +375,8 @@ export class HaServiceControl extends LitElement { )) || serviceData?.description; - return html`${this.hidePicker + return html` + ${this.hidePicker ? nothing : html``} -
    + ${this.hideDescription + ? nothing + : html` +
    + ${description ? html`

    ${description}

    ` : ""} + ${this._manifest + ? html` + + ` + : nothing} +
    + `} ${serviceData && "target" in serviceData ? html` ${hasOptional @@ -517,7 +524,8 @@ export class HaServiceControl extends LitElement { > ` : ""; - })}`; + })} + `; } private _localizeValueCallback = (key: string) => { diff --git a/src/dialogs/more-info/const.ts b/src/dialogs/more-info/const.ts index 65375a6f6d..b9fe72039a 100644 --- a/src/dialogs/more-info/const.ts +++ b/src/dialogs/more-info/const.ts @@ -5,7 +5,7 @@ import { computeGroupDomain, GroupEntity } from "../../data/group"; import { CONTINUOUS_DOMAINS } from "../../data/logbook"; import { HomeAssistant } from "../../types"; -export const DOMAINS_NO_INFO = ["camera", "configurator"]; +export const DOMAINS_NO_INFO = ["camera", "configurator", "script"]; /** * Entity domains that should be editable *if* they have an id present; * {@see shouldShowEditIcon}. diff --git a/src/dialogs/more-info/controls/more-info-script.ts b/src/dialogs/more-info/controls/more-info-script.ts index c53da01be1..aed21e711e 100644 --- a/src/dialogs/more-info/controls/more-info-script.ts +++ b/src/dialogs/more-info/controls/more-info-script.ts @@ -1,8 +1,21 @@ +import "@material/mwc-button"; import { HassEntity } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { + css, + CSSResultGroup, + html, + LitElement, + nothing, + PropertyValues, +} from "lit"; import { customElement, property } from "lit/decorators"; import "../../../components/ha-relative-time"; +import "../../../components/ha-service-control"; +import "../../../components/entity/state-info"; import { HomeAssistant } from "../../../types"; +import { canRun, ScriptEntity } from "../../../data/script"; +import { isUnavailableState } from "../../../data/entity"; +import { computeObjectId } from "../../../common/entity/compute_object_id"; @customElement("more-info-script") class MoreInfoScript extends LitElement { @@ -10,12 +23,61 @@ class MoreInfoScript extends LitElement { @property({ attribute: false }) public stateObj?: HassEntity; + private _scriptData: Record = {}; + protected render() { if (!this.hass || !this.stateObj) { return nothing; } + const stateObj = this.stateObj as ScriptEntity; + + const fields = + this.hass.services.script[computeObjectId(this.stateObj.entity_id)] + ?.fields; + + const hasFields = fields && Object.keys(fields).length > 0; return html` +
    + + ${stateObj.state === "on" + ? html` + ${stateObj.attributes.mode !== "single" && + (stateObj.attributes.current || 0) > 0 + ? this.hass.localize("ui.card.script.cancel_multiple", { + number: stateObj.attributes.current, + }) + : this.hass.localize("ui.card.script.cancel")} + ` + : nothing} + ${stateObj.state === "off" || stateObj.attributes.max + ? html` + ${this.hass!.localize("ui.card.script.run")} + ` + : nothing} +
    + + ${hasFields + ? html` + + ` + : nothing} +
    @@ -36,17 +98,63 @@ class MoreInfoScript extends LitElement { `; } + protected override willUpdate(changedProperties: PropertyValues): void { + super.willUpdate(changedProperties); + + if (!changedProperties.has("stateObj")) { + return; + } + + const oldState = changedProperties.get("stateObj") as + | HassEntity + | undefined; + const newState = this.stateObj; + + if (newState && (!oldState || oldState.entity_id !== newState.entity_id)) { + this._scriptData = { service: newState.entity_id, data: {} }; + } + } + + private _cancelScript(ev: Event) { + ev.stopPropagation(); + this._callService("turn_off"); + } + + private async _runScript(ev: Event) { + ev.stopPropagation(); + this.hass.callService( + "script", + computeObjectId(this.stateObj!.entity_id), + this._scriptData + ); + } + + private _callService(service: string): void { + this.hass.callService("script", service, { + entity_id: this.stateObj!.entity_id, + }); + } + + private _scriptDataChanged(ev: CustomEvent): void { + this._scriptData = { ...this._scriptData, ...ev.detail.value }; + } + static get styles(): CSSResultGroup { return css` .flex { display: flex; justify-content: space-between; + margin-bottom: 16px; } hr { border-color: var(--divider-color); border-bottom: none; margin: 16px 0; } + ha-service-control { + --service-control-padding: 0; + --service-control-items-border-top: none; + } `; } } diff --git a/src/panels/lovelace/entity-rows/hui-script-entity-row.ts b/src/panels/lovelace/entity-rows/hui-script-entity-row.ts index 6fa20512ea..1147d405b4 100644 --- a/src/panels/lovelace/entity-rows/hui-script-entity-row.ts +++ b/src/panels/lovelace/entity-rows/hui-script-entity-row.ts @@ -15,6 +15,8 @@ import { hasConfigOrEntityChanged } from "../common/has-changed"; import "../components/hui-generic-entity-row"; import { createEntityNotFoundWarning } from "../components/hui-warning"; import { ActionRowConfig, LovelaceRow } from "./types"; +import { computeObjectId } from "../../../common/entity/compute_object_id"; +import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog"; @customElement("hui-script-entity-row") class HuiScriptEntityRow extends LitElement implements LovelaceRow { @@ -92,7 +94,15 @@ class HuiScriptEntityRow extends LitElement implements LovelaceRow { private _runScript(ev): void { ev.stopPropagation(); - this._callService("turn_on"); + + const fields = + this.hass!.services.script[computeObjectId(this._config!.entity)]?.fields; + + if (fields && Object.keys(fields).length > 0) { + showMoreInfoDialog(this, { entityId: this._config!.entity }); + } else { + this._callService("turn_on"); + } } private _callService(service: string): void { diff --git a/src/state-summary/state-card-script.ts b/src/state-summary/state-card-script.ts index f8014684e7..fc375d8d25 100644 --- a/src/state-summary/state-card-script.ts +++ b/src/state-summary/state-card-script.ts @@ -8,6 +8,8 @@ import { isUnavailableState } from "../data/entity"; import { canRun, ScriptEntity } from "../data/script"; import { haStyle } from "../resources/styles"; import { HomeAssistant } from "../types"; +import { computeObjectId } from "../common/entity/compute_object_id"; +import { showMoreInfoDialog } from "../dialogs/more-info/show-ha-more-info-dialog"; @customElement("state-card-script") class StateCardScript extends LitElement { @@ -56,7 +58,16 @@ class StateCardScript extends LitElement { private _runScript(ev: Event) { ev.stopPropagation(); - this._callService("turn_on"); + + const fields = + this.hass!.services.script[computeObjectId(this.stateObj.entity_id)] + ?.fields; + + if (fields && Object.keys(fields).length > 0) { + showMoreInfoDialog(this, { entityId: this.stateObj.entity_id }); + } else { + this._callService("turn_on"); + } } private _callService(service: string): void { From 45e09a262b6be5dfd933ee0d7f144f454cb017d0 Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Tue, 27 Feb 2024 09:20:13 -0500 Subject: [PATCH 082/116] Fix issues with state_color as false (#19776) * Fix issues with state_color as false * Remove format from glance timestamp * Restore type assertion hack and remove conditional * Revert "removal of glance timestamp format and adjust types to make it work * Revert to minimal change just to pass false state_color --- src/components/entity/state-badge.ts | 6 ++++-- src/panels/lovelace/cards/hui-button-card.ts | 6 +----- src/panels/lovelace/cards/hui-entities-card.ts | 4 ++-- src/panels/lovelace/cards/hui-entity-card.ts | 6 +----- src/panels/lovelace/cards/hui-glance-card.ts | 17 ++++++++--------- src/panels/lovelace/cards/types.ts | 6 ++++-- 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/components/entity/state-badge.ts b/src/components/entity/state-badge.ts index 75d077ed51..13b874a5e4 100644 --- a/src/components/entity/state-badge.ts +++ b/src/components/entity/state-badge.ts @@ -32,7 +32,9 @@ export class StateBadge extends LitElement { @property() public overrideImage?: string; - @property({ type: Boolean }) public stateColor = false; + // Cannot be a boolean attribute because undefined is treated different than + // false. When it is undefined, state is still colored for light entities. + @property({ attribute: false }) public stateColor?: boolean; @property() public color?: string; @@ -70,7 +72,7 @@ export class StateBadge extends LitElement { const domain = this.stateObj ? computeStateDomain(this.stateObj) : undefined; - return this.stateColor || (domain === "light" && this.stateColor !== false); + return this.stateColor ?? domain === "light"; } protected render() { diff --git a/src/panels/lovelace/cards/hui-button-card.ts b/src/panels/lovelace/cards/hui-button-card.ts index 2d440a3036..3a37329b27 100644 --- a/src/panels/lovelace/cards/hui-button-card.ts +++ b/src/panels/lovelace/cards/hui-button-card.ts @@ -134,11 +134,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard { private getStateColor(stateObj: HassEntity, config: ButtonCardConfig) { const domain = stateObj ? computeStateDomain(stateObj) : undefined; - return ( - config && - (config.state_color || - (domain === "light" && config.state_color !== false)) - ); + return config && (config.state_color ?? domain === "light"); } public getCardSize(): number { diff --git a/src/panels/lovelace/cards/hui-entities-card.ts b/src/panels/lovelace/cards/hui-entities-card.ts index 3d5b46449e..4fb12c4661 100644 --- a/src/panels/lovelace/cards/hui-entities-card.ts +++ b/src/panels/lovelace/cards/hui-entities-card.ts @@ -297,9 +297,9 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard { private renderEntity(entityConf: LovelaceRowConfig): TemplateResult { const element = createRowElement( (!("type" in entityConf) || entityConf.type === "conditional") && - this._config!.state_color + "state_color" in this._config! ? ({ - state_color: true, + state_color: this._config.state_color, ...(entityConf as EntityConfig), } as EntityConfig) : entityConf diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index fd1b63e13e..e6366cd28b 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -75,11 +75,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { private getStateColor(stateObj: HassEntity, config: EntityCardConfig) { const domain = stateObj ? computeStateDomain(stateObj) : undefined; - return ( - config && - (config.state_color || - (domain === "light" && config.state_color !== false)) - ); + return config && (config.state_color ?? domain === "light"); } public setConfig(config: EntityCardConfig): void { diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index d48fb68cfe..8217da7501 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -89,12 +89,12 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { state_color: true, ...config, }; - const entities = processConfigEntities( - config.entities - ).map((entityConf) => ({ - hold_action: { action: "more-info" } as MoreInfoActionConfig, - ...entityConf, - })); + const entities = processConfigEntities(config.entities).map( + (entityConf) => ({ + hold_action: { action: "more-info" } as MoreInfoActionConfig, + ...entityConf, + }) + ); for (const entity of entities) { if ( @@ -237,7 +237,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { `; } - private renderEntity(entityConf): TemplateResult { + private renderEntity(entityConf: GlanceConfigEntity): TemplateResult { const stateObj = this.hass!.states[entityConf.entity]; if (!stateObj) { @@ -294,8 +294,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { .stateObj=${stateObj} .overrideIcon=${entityConf.icon} .overrideImage=${entityConf.image} - .stateColor=${(entityConf.state_color === false || - entityConf.state_color) ?? + .stateColor=${entityConf.state_color ?? this._config!.state_color} > ` diff --git a/src/panels/lovelace/cards/types.ts b/src/panels/lovelace/cards/types.ts index 8a57d1b0d5..9c353c02b6 100644 --- a/src/panels/lovelace/cards/types.ts +++ b/src/panels/lovelace/cards/types.ts @@ -4,8 +4,10 @@ import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import { Statistic, StatisticType } from "../../../data/recorder"; import { ForecastType } from "../../../data/weather"; import { FullCalendarView, TranslationDict } from "../../../types"; +import { LovelaceCardFeatureConfig } from "../card-features/types"; import { Condition, LegacyCondition } from "../common/validate-condition"; import { HuiImage } from "../components/hui-image"; +import { TimestampRenderingFormat } from "../components/types"; import { LovelaceElementConfig } from "../elements/types"; import { EntityConfig, @@ -13,7 +15,6 @@ import { LovelaceRowConfig, } from "../entity-rows/types"; import { LovelaceHeaderFooterConfig } from "../header-footer/types"; -import { LovelaceCardFeatureConfig } from "../card-features/types"; export type AlarmPanelCardConfigState = | "arm_away" @@ -252,6 +253,7 @@ export interface GlanceConfigEntity extends ConfigEntity { image?: string; show_state?: boolean; state_color?: boolean; + format: TimestampRenderingFormat; } export interface GlanceCardConfig extends LovelaceCardConfig { @@ -260,7 +262,7 @@ export interface GlanceCardConfig extends LovelaceCardConfig { show_icon?: boolean; title?: string; theme?: string; - entities: Array; + entities: (string | GlanceConfigEntity)[]; columns?: number; state_color?: boolean; } From dd8c1d359c50b47e28fdf43e7b1f66122c398183 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 27 Feb 2024 09:22:34 -0500 Subject: [PATCH 083/116] localize input_select form (#19829) --- src/panels/config/helpers/forms/ha-input_select-form.ts | 9 +++++++-- src/translations/en.json | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/panels/config/helpers/forms/ha-input_select-form.ts b/src/panels/config/helpers/forms/ha-input_select-form.ts index e5a16fb54a..47ebdc6e32 100644 --- a/src/panels/config/helpers/forms/ha-input_select-form.ts +++ b/src/panels/config/helpers/forms/ha-input_select-form.ts @@ -177,8 +177,13 @@ class HaInputSelectForm extends LitElement { const index = (ev.target as any).index; if ( !(await showConfirmationDialog(this, { - title: "Delete this item?", - text: "Are you sure you want to delete this item?", + title: this.hass.localize( + "ui.dialogs.helper_settings.input_select.confirm_delete.delete" + ), + text: this.hass.localize( + "ui.dialogs.helper_settings.input_select.confirm_delete.prompt" + ), + destructive: true, })) ) { return; diff --git a/src/translations/en.json b/src/translations/en.json index 972c6753f5..c5a3c72808 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1371,7 +1371,11 @@ "add_option": "[%key:ui::panel::config::automation::editor::actions::type::choose::add_option%]", "remove_option": "[%key:ui::panel::config::automation::editor::actions::type::choose::remove_option%]", "no_options": "There are no options yet.", - "add": "Add" + "add": "Add", + "confirm_delete": { + "delete": "[%key:ui::components::todo::item::confirm_delete::delete%]", + "prompt": "[%key:ui::components::todo::item::confirm_delete::prompt%]" + } }, "counter": { "minimum": "Minimum value", From fb3c94f4039bda8f235a50837321b657caa1d00f Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 27 Feb 2024 09:24:42 -0500 Subject: [PATCH 084/116] Improve map/zone navigation (#19740) * Improve map/zone navigation * dont remove tabs --- src/panels/config/zone/ha-config-zone.ts | 6 +++++- src/panels/map/ha-panel-map.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/panels/config/zone/ha-config-zone.ts b/src/panels/config/zone/ha-config-zone.ts index bf56ed7ce5..2455bf2b48 100644 --- a/src/panels/config/zone/ha-config-zone.ts +++ b/src/panels/config/zone/ha-config-zone.ts @@ -58,6 +58,8 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { @property({ attribute: false }) public route!: Route; + @state() private _searchParms = new URLSearchParams(window.location.search); + @state() private _storageItems?: Zone[]; @state() private _stateItems?: HassEntity[]; @@ -219,7 +221,9 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { .hass=${this.hass} .narrow=${this.narrow} .route=${this.route} - back-path="/config" + .backPath=${this._searchParms.has("historyBack") + ? undefined + : "/config"} .tabs=${configSections.areas} > ${this.narrow diff --git a/src/panels/map/ha-panel-map.ts b/src/panels/map/ha-panel-map.ts index b7698ca186..d6c1cbd1fb 100644 --- a/src/panels/map/ha-panel-map.ts +++ b/src/panels/map/ha-panel-map.ts @@ -46,7 +46,7 @@ class HaPanelMap extends LitElement { } private _openZonesEditor() { - navigate("/config/zone"); + navigate("/config/zone?historyBack=1"); } public willUpdate(changedProps: PropertyValues) { From 29954e530e62ab3cc2788fb5ccf812d12d84899d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Tue, 27 Feb 2024 15:36:16 +0100 Subject: [PATCH 085/116] Hide the core backup integration if hassio is loaded (#19833) * Hide the core backup integration if hassio is loaded * Combine to single entry --- src/common/config/can_show_page.ts | 11 ++++++++++- src/layouts/hass-tabs-subpage.ts | 1 + src/panels/config/ha-panel-config.ts | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/common/config/can_show_page.ts b/src/common/config/can_show_page.ts index 0e4fdd0ecd..322839010f 100644 --- a/src/common/config/can_show_page.ts +++ b/src/common/config/can_show_page.ts @@ -1,10 +1,12 @@ import { PageNavigation } from "../../layouts/hass-tabs-subpage"; import { HomeAssistant } from "../../types"; +import { ensureArray } from "../array/ensure-array"; import { isComponentLoaded } from "./is_component_loaded"; export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => (isCore(page) || isLoadedIntegration(hass, page)) && - !hideAdvancedPage(hass, page); + !hideAdvancedPage(hass, page) && + isNotLoadedIntegration(hass, page); const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) => page.component @@ -14,6 +16,13 @@ const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) => isComponentLoaded(hass, integration) ) : true; + +const isNotLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) => + !page.not_component || + !ensureArray(page.not_component).some((integration) => + isComponentLoaded(hass, integration) + ); + const isCore = (page: PageNavigation) => page.core; const isAdvancedPage = (page: PageNavigation) => page.advancedOnly; const userWantsAdvanced = (hass: HomeAssistant) => hass.userData?.showAdvanced; diff --git a/src/layouts/hass-tabs-subpage.ts b/src/layouts/hass-tabs-subpage.ts index 9de45b8a4a..9d91dfb858 100644 --- a/src/layouts/hass-tabs-subpage.ts +++ b/src/layouts/hass-tabs-subpage.ts @@ -26,6 +26,7 @@ export interface PageNavigation { component?: string; components?: string[]; name?: string; + not_component?: string | string[]; core?: boolean; advancedOnly?: boolean; iconPath?: string; diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index e6aacf6fd5..0b56058a93 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -309,6 +309,7 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconPath: mdiBackupRestore, iconColor: "#0D47A1", component: "backup", + not_component: "hassio", }, { path: "/hassio/backups", From babb7235210107ea42af19162a1e1350f8bdfc44 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 27 Feb 2024 16:05:27 -0500 Subject: [PATCH 086/116] Fix script data (#19894) --- src/dialogs/more-info/controls/more-info-script.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogs/more-info/controls/more-info-script.ts b/src/dialogs/more-info/controls/more-info-script.ts index aed21e711e..a2fc2b1186 100644 --- a/src/dialogs/more-info/controls/more-info-script.ts +++ b/src/dialogs/more-info/controls/more-info-script.ts @@ -125,7 +125,7 @@ class MoreInfoScript extends LitElement { this.hass.callService( "script", computeObjectId(this.stateObj!.entity_id), - this._scriptData + this._scriptData.data ); } From a479c6e7860dd1f933904773de23ecd9f2609884 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Tue, 27 Feb 2024 23:02:39 -0500 Subject: [PATCH 087/116] Download energy panel data to CSV (#19863) * Download energy panel data to CSV * table format changes * unique types for cost/compensation --- src/data/energy.ts | 2 +- src/panels/energy/ha-panel-energy.ts | 199 ++++++++++++++++++++++++++- src/translations/en.json | 1 + 3 files changed, 200 insertions(+), 2 deletions(-) diff --git a/src/data/energy.ts b/src/data/energy.ts index 32340c0209..a811199cee 100644 --- a/src/data/energy.ts +++ b/src/data/energy.ts @@ -781,7 +781,7 @@ export const getEnergyGasUnit = ( : "ftÂł"; }; -export const getEnergyWaterUnit = (hass: HomeAssistant): string | undefined => +export const getEnergyWaterUnit = (hass: HomeAssistant): string => hass.config.unit_system.length === "km" ? "L" : "gal"; export const energyStatisticHelpUrl = diff --git a/src/panels/energy/ha-panel-energy.ts b/src/panels/energy/ha-panel-energy.ts index a9462d0edb..4dd6498ae4 100644 --- a/src/panels/energy/ha-panel-energy.ts +++ b/src/panels/energy/ha-panel-energy.ts @@ -7,7 +7,7 @@ import { html, nothing, } from "lit"; -import { mdiPencil } from "@mdi/js"; +import { mdiPencil, mdiDownload } from "@mdi/js"; import { customElement, property, state } from "lit/decorators"; import "../../components/ha-menu-button"; import "../../components/ha-list-item"; @@ -19,6 +19,18 @@ import "../lovelace/components/hui-energy-period-selector"; import { Lovelace } from "../lovelace/types"; import "../lovelace/views/hui-view"; import { navigate } from "../../common/navigate"; +import { + getEnergyDataCollection, + getEnergyGasUnit, + getEnergyWaterUnit, + GridSourceTypeEnergyPreference, + SolarSourceTypeEnergyPreference, + BatterySourceTypeEnergyPreference, + GasSourceTypeEnergyPreference, + WaterSourceTypeEnergyPreference, + DeviceConsumptionEnergyPreference, +} from "../../data/energy"; +import { fileDownload } from "../../util/file_download"; const ENERGY_LOVELACE_CONFIG: LovelaceConfig = { views: [ @@ -86,6 +98,15 @@ class PanelEnergy extends LitElement { ${this.hass!.localize("ui.panel.energy.configure")} + + + + ${this.hass!.localize("ui.panel.energy.download_data")} + ` : nothing} @@ -122,6 +143,182 @@ class PanelEnergy extends LitElement { navigate("/config/energy?historyBack=1"); } + private async _dumpCSV(ev) { + ev.stopPropagation(); + const energyData = getEnergyDataCollection(this.hass, { + key: "energy_dashboard", + }); + + if (!energyData.prefs || !energyData.state.stats) { + return; + } + + const gasUnit = + getEnergyGasUnit( + this.hass, + energyData.prefs, + energyData.state.statsMetadata + ) || ""; + const waterUnit = getEnergyWaterUnit(this.hass); + const electricUnit = "kWh"; + + const energy_sources = energyData.prefs.energy_sources; + const device_consumption = energyData.prefs.device_consumption; + const stats = energyData.state.stats; + + const timeSet = new Set(); + Object.values(stats).forEach((stat) => { + stat.forEach((datapoint) => { + timeSet.add(datapoint.start); + }); + }); + const times = Array.from(timeSet).sort(); + + const headers = + "entity_id,type,unit," + + times.map((t) => new Date(t).toISOString()).join(",") + + "\n"; + const csv: string[] = []; + csv[0] = headers; + + const processStat = function (stat: string, type: string, unit: string) { + let n = 0; + const row: string[] = []; + if (!stats[stat]) { + return; + } + row.push(stat); + row.push(type); + row.push(unit.normalize("NFKD")); + times.forEach((t) => { + if (stats[stat][n].start > t) { + row.push(""); + } else if (n < stats[stat].length && stats[stat][n].start === t) { + row.push((stats[stat][n].change ?? "").toString()); + n++; + } else { + row.push(""); + } + }); + csv.push(row.join(",") + "\n"); + }; + + const currency = this.hass.config.currency; + + const printCategory = function ( + type: string, + statIds: string[], + unit: string, + costType?: string + ) { + if (statIds.length) { + statIds.forEach((stat) => processStat(stat, type, unit)); + if (costType) { + statIds.forEach((stat) => { + const costStat = energyData.state.info.cost_sensors[stat]; + if (energyData.state.info.cost_sensors[stat]) { + processStat(costStat, costType, currency); + } + }); + } + } + }; + + const grid_consumptions: string[] = []; + const grid_productions: string[] = []; + energy_sources + .filter((s) => s.type === "grid") + .forEach((source) => { + source = source as GridSourceTypeEnergyPreference; + source.flow_from.forEach((flowFrom) => { + grid_consumptions.push(flowFrom.stat_energy_from); + }); + source.flow_to.forEach((flowTo) => { + grid_productions.push(flowTo.stat_energy_to); + }); + }); + + printCategory( + "grid_consumption", + grid_consumptions, + electricUnit, + "grid_consumption_cost" + ); + printCategory( + "grid_return", + grid_productions, + electricUnit, + "grid_return_compensation" + ); + + const battery_ins: string[] = []; + const battery_outs: string[] = []; + energy_sources + .filter((s) => s.type === "battery") + .forEach((source) => { + source = source as BatterySourceTypeEnergyPreference; + battery_ins.push(source.stat_energy_to); + battery_outs.push(source.stat_energy_from); + }); + + printCategory("battery_in", battery_ins, electricUnit); + printCategory("battery_out", battery_outs, electricUnit); + + const solar_productions: string[] = []; + energy_sources + .filter((s) => s.type === "solar") + .forEach((source) => { + source = source as SolarSourceTypeEnergyPreference; + solar_productions.push(source.stat_energy_from); + }); + + printCategory("solar_production", solar_productions, electricUnit); + + const gas_consumptions: string[] = []; + energy_sources + .filter((s) => s.type === "gas") + .forEach((source) => { + source = source as GasSourceTypeEnergyPreference; + gas_consumptions.push(source.stat_energy_from); + }); + + printCategory( + "gas_consumption", + gas_consumptions, + gasUnit, + "gas_consumption_cost" + ); + + const water_consumptions: string[] = []; + energy_sources + .filter((s) => s.type === "water") + .forEach((source) => { + source = source as WaterSourceTypeEnergyPreference; + water_consumptions.push(source.stat_energy_from); + }); + + printCategory( + "water_consumption", + water_consumptions, + waterUnit, + "water_consumption_cost" + ); + + const devices: string[] = []; + device_consumption.forEach((source) => { + source = source as DeviceConsumptionEnergyPreference; + devices.push(source.stat_consumption); + }); + + printCategory("device_consumption", devices, electricUnit); + + const blob = new Blob(csv, { + type: "text/csv", + }); + const url = window.URL.createObjectURL(blob); + fileDownload(url, "energy.csv"); + } + private _reloadView() { // Force strategy to be re-run by make a copy of the view const config = this._lovelace!.config; diff --git a/src/translations/en.json b/src/translations/en.json index c5a3c72808..1ad7fed83a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6538,6 +6538,7 @@ } }, "energy": { + "download_data": "[%key:ui::panel::history::download_data%]", "configure": "[%key:ui::dialogs::quick-bar::commands::navigation::energy%]", "compare": { "info": "You are comparing the period {start} with the period {end}" From 841b9c0917a7c7efed7a31a77a08a4728ff7118e Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 28 Feb 2024 11:12:19 +0100 Subject: [PATCH 088/116] Add support for translation domain in data entry flows (#19900) --- src/data/data_entry_flow.ts | 6 ++++++ .../config-flow/show-dialog-config-flow.ts | 20 +++++++++---------- .../config-flow/show-dialog-options-flow.ts | 16 +++++++-------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/data/data_entry_flow.ts b/src/data/data_entry_flow.ts index 7a830e7d88..d60e55beab 100644 --- a/src/data/data_entry_flow.ts +++ b/src/data/data_entry_flow.ts @@ -33,6 +33,7 @@ export interface DataEntryFlowStepForm { description_placeholders?: Record; last_step: boolean | null; preview?: string; + translation_domain?: string; } export interface DataEntryFlowStepExternal { @@ -42,6 +43,7 @@ export interface DataEntryFlowStepExternal { step_id: string; url: string; description_placeholders: Record; + translation_domain?: string; } export interface DataEntryFlowStepCreateEntry { @@ -53,6 +55,7 @@ export interface DataEntryFlowStepCreateEntry { result?: ConfigEntry; description: string; description_placeholders?: Record; + translation_domain?: string; } export interface DataEntryFlowStepAbort { @@ -61,6 +64,7 @@ export interface DataEntryFlowStepAbort { handler: string; reason: string; description_placeholders?: Record; + translation_domain?: string; } export interface DataEntryFlowStepProgress { @@ -70,6 +74,7 @@ export interface DataEntryFlowStepProgress { step_id: string; progress_action: string; description_placeholders?: Record; + translation_domain?: string; } export interface DataEntryFlowStepMenu { @@ -80,6 +85,7 @@ export interface DataEntryFlowStepMenu { /** If array, use value to lookup translations in strings.json */ menu_options: string[] | Record; description_placeholders?: Record; + translation_domain?: string; } export type DataEntryFlowStep = diff --git a/src/dialogs/config-flow/show-dialog-config-flow.ts b/src/dialogs/config-flow/show-dialog-config-flow.ts index 9ccc581840..8b2b6b1667 100644 --- a/src/dialogs/config-flow/show-dialog-config-flow.ts +++ b/src/dialogs/config-flow/show-dialog-config-flow.ts @@ -44,7 +44,7 @@ export const showConfigFlowDialog = ( renderAbortDescription(hass, step) { const description = hass.localize( - `component.${step.handler}.config.abort.${step.reason}`, + `component.${step.translation_domain || step.handler}.config.abort.${step.reason}`, step.description_placeholders ); @@ -58,7 +58,7 @@ export const showConfigFlowDialog = ( renderShowFormStepHeader(hass, step) { return ( hass.localize( - `component.${step.handler}.config.step.${step.step_id}.title`, + `component.${step.translation_domain || step.handler}.config.step.${step.step_id}.title`, step.description_placeholders ) || hass.localize(`component.${step.handler}.title`) ); @@ -66,7 +66,7 @@ export const showConfigFlowDialog = ( renderShowFormStepDescription(hass, step) { const description = hass.localize( - `component.${step.handler}.config.step.${step.step_id}.description`, + `component.${step.translation_domain || step.handler}.config.step.${step.step_id}.description`, step.description_placeholders ); return description @@ -84,7 +84,7 @@ export const showConfigFlowDialog = ( renderShowFormStepFieldHelper(hass, step, field) { const description = hass.localize( - `component.${step.handler}.config.step.${step.step_id}.data_description.${field.name}`, + `component.${step.translation_domain || step.handler}.config.step.${step.step_id}.data_description.${field.name}`, step.description_placeholders ); return description @@ -95,7 +95,7 @@ export const showConfigFlowDialog = ( renderShowFormStepFieldError(hass, step, error) { return ( hass.localize( - `component.${step.handler}.config.error.${error}`, + `component.${step.translation_domain || step.translation_domain || step.handler}.config.error.${error}`, step.description_placeholders ) || error ); @@ -131,7 +131,7 @@ export const showConfigFlowDialog = ( renderExternalStepDescription(hass, step) { const description = hass.localize( - `component.${step.handler}.config.${step.step_id}.description`, + `component.${step.translation_domain || step.handler}.config.${step.step_id}.description`, step.description_placeholders ); @@ -155,7 +155,7 @@ export const showConfigFlowDialog = ( renderCreateEntryDescription(hass, step) { const description = hass.localize( - `component.${step.handler}.config.create_entry.${ + `component.${step.translation_domain || step.handler}.config.create_entry.${ step.description || "default" }`, step.description_placeholders @@ -190,7 +190,7 @@ export const showConfigFlowDialog = ( renderShowFormProgressDescription(hass, step) { const description = hass.localize( - `component.${step.handler}.config.progress.${step.progress_action}`, + `component.${step.translation_domain || step.handler}.config.progress.${step.progress_action}`, step.description_placeholders ); return description @@ -210,7 +210,7 @@ export const showConfigFlowDialog = ( renderMenuDescription(hass, step) { const description = hass.localize( - `component.${step.handler}.config.step.${step.step_id}.description`, + `component.${step.translation_domain || step.handler}.config.step.${step.step_id}.description`, step.description_placeholders ); return description @@ -222,7 +222,7 @@ export const showConfigFlowDialog = ( renderMenuOption(hass, step, option) { return hass.localize( - `component.${step.handler}.config.step.${step.step_id}.menu_options.${option}`, + `component.${step.translation_domain || step.handler}.config.step.${step.step_id}.menu_options.${option}`, step.description_placeholders ); }, diff --git a/src/dialogs/config-flow/show-dialog-options-flow.ts b/src/dialogs/config-flow/show-dialog-options-flow.ts index 05f3acc1b4..491df7e91f 100644 --- a/src/dialogs/config-flow/show-dialog-options-flow.ts +++ b/src/dialogs/config-flow/show-dialog-options-flow.ts @@ -53,7 +53,7 @@ export const showOptionsFlowDialog = ( renderAbortDescription(hass, step) { const description = hass.localize( - `component.${configEntry.domain}.options.abort.${step.reason}`, + `component.${step.translation_domain || configEntry.domain}.options.abort.${step.reason}`, step.description_placeholders ); @@ -71,7 +71,7 @@ export const showOptionsFlowDialog = ( renderShowFormStepHeader(hass, step) { return ( hass.localize( - `component.${configEntry.domain}.options.step.${step.step_id}.title`, + `component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.title`, step.description_placeholders ) || hass.localize(`ui.dialogs.options_flow.form.header`) ); @@ -79,7 +79,7 @@ export const showOptionsFlowDialog = ( renderShowFormStepDescription(hass, step) { const description = hass.localize( - `component.${configEntry.domain}.options.step.${step.step_id}.description`, + `component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.description`, step.description_placeholders ); return description @@ -101,7 +101,7 @@ export const showOptionsFlowDialog = ( renderShowFormStepFieldHelper(hass, step, field) { const description = hass.localize( - `component.${configEntry.domain}.options.step.${step.step_id}.data_description.${field.name}`, + `component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.data_description.${field.name}`, step.description_placeholders ); return description @@ -112,7 +112,7 @@ export const showOptionsFlowDialog = ( renderShowFormStepFieldError(hass, step, error) { return ( hass.localize( - `component.${configEntry.domain}.options.error.${error}`, + `component.${step.translation_domain || configEntry.domain}.options.error.${error}`, step.description_placeholders ) || error ); @@ -159,7 +159,7 @@ export const showOptionsFlowDialog = ( renderShowFormProgressDescription(hass, step) { const description = hass.localize( - `component.${configEntry.domain}.options.progress.${step.progress_action}`, + `component.${step.translation_domain || configEntry.domain}.options.progress.${step.progress_action}`, step.description_placeholders ); return description @@ -183,7 +183,7 @@ export const showOptionsFlowDialog = ( renderMenuDescription(hass, step) { const description = hass.localize( - `component.${configEntry.domain}.options.step.${step.step_id}.description`, + `component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.description`, step.description_placeholders ); return description @@ -199,7 +199,7 @@ export const showOptionsFlowDialog = ( renderMenuOption(hass, step, option) { return hass.localize( - `component.${configEntry.domain}.options.step.${step.step_id}.menu_options.${option}`, + `component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.menu_options.${option}`, step.description_placeholders ); }, From 3afc218adc66d0d43e59da91e87cda222e07a6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 28 Feb 2024 11:25:41 +0100 Subject: [PATCH 089/116] Combine component/components PageNavigation keys (#19890) * Combine component/components PageNavigation keys * Update src/panels/config/ha-panel-config.ts --- src/common/config/can_show_page.ts | 11 ++++------- src/layouts/hass-tabs-subpage.ts | 15 +++------------ src/panels/config/ha-panel-config.ts | 6 +++--- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/common/config/can_show_page.ts b/src/common/config/can_show_page.ts index 322839010f..d73ebc5e2e 100644 --- a/src/common/config/can_show_page.ts +++ b/src/common/config/can_show_page.ts @@ -9,13 +9,10 @@ export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => isNotLoadedIntegration(hass, page); const isLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) => - page.component - ? isComponentLoaded(hass, page.component) - : page.components - ? page.components.some((integration) => - isComponentLoaded(hass, integration) - ) - : true; + !page.component || + ensureArray(page.component).some((integration) => + isComponentLoaded(hass, integration) + ); const isNotLoadedIntegration = (hass: HomeAssistant, page: PageNavigation) => !page.not_component || diff --git a/src/layouts/hass-tabs-subpage.ts b/src/layouts/hass-tabs-subpage.ts index 9d91dfb858..3fe46f8456 100644 --- a/src/layouts/hass-tabs-subpage.ts +++ b/src/layouts/hass-tabs-subpage.ts @@ -10,7 +10,6 @@ import { import { customElement, eventOptions, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { isComponentLoaded } from "../common/config/is_component_loaded"; import { restoreScroll } from "../common/decorators/restore-scroll"; import { LocalizeFunc } from "../common/translations/localize"; import "../components/ha-icon-button-arrow-prev"; @@ -19,12 +18,12 @@ import "../components/ha-svg-icon"; import "../components/ha-tab"; import { HomeAssistant, Route } from "../types"; import { haStyleScrollbar } from "../resources/styles"; +import { canShowPage } from "../common/config/can_show_page"; export interface PageNavigation { path: string; translationKey?: string; - component?: string; - components?: string[]; + component?: string | string[]; name?: string; not_component?: string | string[]; core?: boolean; @@ -67,19 +66,12 @@ class HassTabsSubpage extends LitElement { ( tabs: PageNavigation[], activeTab: PageNavigation | undefined, - showAdvanced: boolean | undefined, _components, _language, _narrow, localizeFunc ) => { - const shownTabs = tabs.filter( - (page) => - (!page.component || - page.core || - isComponentLoaded(this.hass, page.component)) && - (!page.advancedOnly || showAdvanced) - ); + const shownTabs = tabs.filter((page) => canShowPage(this.hass, page)); if (shownTabs.length < 2) { if (shownTabs.length === 1) { @@ -128,7 +120,6 @@ class HassTabsSubpage extends LitElement { const tabs = this._getTabs( this.tabs, this._activeTab, - this.hass.userData?.showAdvanced, this.hass.config.components, this.hass.language, this.narrow, diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index 0b56058a93..c0028557ec 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -74,7 +74,7 @@ export const configSections: { [name: string]: PageNavigation[] } = { translationKey: "areas", iconPath: mdiSofa, iconColor: "#E48629", - components: ["zone"], + component: "zone", }, { path: "/hassio", @@ -108,7 +108,7 @@ export const configSections: { [name: string]: PageNavigation[] } = { translationKey: "people", iconPath: mdiAccount, iconColor: "#5A87FA", - components: ["person", "users"], + component: ["person", "users"], }, { path: "#external-app-configuration", @@ -342,7 +342,7 @@ export const configSections: { [name: string]: PageNavigation[] } = { translationKey: "hardware", iconPath: mdiMemory, iconColor: "#301A8E", - components: ["hassio", "hardware"], + component: ["hassio", "hardware"], }, ], about: [ From 0892ed18e5f2b45878a835caf018f12a28916d57 Mon Sep 17 00:00:00 2001 From: Yosi Levy <37745463+yosilevy@users.noreply.github.com> Date: Wed, 28 Feb 2024 12:26:18 +0200 Subject: [PATCH 090/116] RTL fixes (#19834) * RTL fixes * Additional update * Fixed comments --- hassio/src/addon-view/info/hassio-addon-info.ts | 1 + hassio/src/dialogs/hardware/dialog-hassio-hardware.ts | 4 ++++ src/components/data-table/ha-data-table-icon.ts | 2 ++ src/components/ha-control-number-buttons.ts | 4 ++++ src/components/ha-menu-button.ts | 2 ++ src/components/ha-sidebar.ts | 6 ++++-- src/panels/config/hardware/dialog-hardware-available.ts | 4 ++++ 7 files changed, 21 insertions(+), 2 deletions(-) diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index 8b183fc51d..e90b447de6 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -1263,6 +1263,7 @@ class HassioAddonInfo extends LitElement { .card-actions { justify-content: space-between; display: flex; + direction: var(--direction); } .changelog { display: contents; diff --git a/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts b/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts index b4904b83cc..62298180ae 100644 --- a/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts +++ b/hassio/src/dialogs/hardware/dialog-hassio-hardware.ts @@ -154,12 +154,16 @@ class HassioHardwareDialog extends LitElement { ha-icon-button { position: absolute; right: 16px; + inset-inline-end: 16px; + inset-inline-start: initial; top: 10px; text-decoration: none; color: var(--primary-text-color); } h2 { margin: 18px 42px 0 18px; + margin-inline-start: 18px; + margin-inline-end: 42px; color: var(--primary-text-color); } diff --git a/src/components/data-table/ha-data-table-icon.ts b/src/components/data-table/ha-data-table-icon.ts index 5e7cada2d5..8724b41fbb 100644 --- a/src/components/data-table/ha-data-table-icon.ts +++ b/src/components/data-table/ha-data-table-icon.ts @@ -44,6 +44,8 @@ class HaDataTableIcon extends LitElement { div { position: absolute; right: 28px; + inset-inline-end: 28px; + inset-inline-start: initial; z-index: 1002; outline: none; font-size: 10px; diff --git a/src/components/ha-control-number-buttons.ts b/src/components/ha-control-number-buttons.ts index 36172f4e08..5ee5dc0d1a 100644 --- a/src/components/ha-control-number-buttons.ts +++ b/src/components/ha-control-number-buttons.ts @@ -273,9 +273,13 @@ export class HaControlNumberButton extends LitElement { } .button.minus { left: 0; + inset-inline-start: 0; + inset-inline-end: initial; } .button.plus { right: 0; + inset-inline-start: initial; + inset-inline-end: 0; } .unit { white-space: pre; diff --git a/src/components/ha-menu-button.ts b/src/components/ha-menu-button.ts index 63481ad1fd..4c70956dd1 100644 --- a/src/components/ha-menu-button.ts +++ b/src/components/ha-menu-button.ts @@ -136,6 +136,8 @@ class HaMenuButton extends LitElement { height: 12px; top: 9px; right: 7px; + inset-inline-end: 7px; + inset-inline-start: initial; border-radius: 50%; border: 2px solid var(--app-header-background-color); } diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts index d36a6a646d..6110c85468 100644 --- a/src/components/ha-sidebar.ts +++ b/src/components/ha-sidebar.ts @@ -1010,8 +1010,8 @@ class HaSidebar extends SubscribeMixin(LitElement) { } .profile paper-icon-item { padding-left: 4px; - margin-inline-start: 4px; - margin-inline-end: auto; + padding-inline-start: 4px; + padding-inline-end: auto; } .profile .item-text { margin-left: 8px; @@ -1040,6 +1040,8 @@ class HaSidebar extends SubscribeMixin(LitElement) { position: absolute; bottom: 14px; left: 26px; + inset-inline-start: 26px; + inset-inline-end: initial; font-size: 0.65em; } diff --git a/src/panels/config/hardware/dialog-hardware-available.ts b/src/panels/config/hardware/dialog-hardware-available.ts index 5cc858b1c8..3dfd602367 100644 --- a/src/panels/config/hardware/dialog-hardware-available.ts +++ b/src/panels/config/hardware/dialog-hardware-available.ts @@ -171,6 +171,8 @@ class DialogHardwareAvailable extends LitElement implements HassDialog { ha-icon-button { position: absolute; right: 16px; + inset-inline-end: 16px; + inset-inline-start: initial; top: 10px; inset-inline-end: 16px; inset-inline-start: initial; @@ -179,6 +181,8 @@ class DialogHardwareAvailable extends LitElement implements HassDialog { } h2 { margin: 18px 42px 0 18px; + margin-inline-start: 18px; + margin-inline-end: 42px; color: var(--primary-text-color); } ha-expansion-panel { From f4c932ef9c2f5f2080522921f1f472077f26b9cd Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 Feb 2024 11:35:19 +0100 Subject: [PATCH 091/116] Automatically add section to empty section view and improve delete wording (#19892) * Automatically add section to empty section view and improve delete wording * Update delete dialog * Update delete dialog --- .../view-editor/hui-dialog-edit-view.ts | 84 ++++++++++--------- .../lovelace/views/hui-sections-view.ts | 31 ++----- src/translations/en.json | 19 +++-- 3 files changed, 64 insertions(+), 70 deletions(-) diff --git a/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts b/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts index 22583a3595..038680b049 100644 --- a/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts +++ b/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts @@ -4,16 +4,16 @@ import { mdiCheck, mdiClose, mdiDotsVertical } from "@mdi/js"; import "@polymer/paper-tabs/paper-tab"; import "@polymer/paper-tabs/paper-tabs"; import { - css, CSSResultGroup, - html, LitElement, - nothing, PropertyValues, + css, + html, + nothing, } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; -import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; +import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event"; import { stopPropagation } from "../../../../common/dom/stop_propagation"; import { navigate } from "../../../../common/navigate"; import { deepEqual } from "../../../../common/util/deep-equal"; @@ -23,6 +23,11 @@ import "../../../../components/ha-dialog"; import "../../../../components/ha-dialog-header"; import "../../../../components/ha-yaml-editor"; import type { HaYamlEditor } from "../../../../components/ha-yaml-editor"; +import { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge"; +import { + LovelaceViewConfig, + isStrategyView, +} from "../../../../data/lovelace/config/view"; import { showAlertDialog, showConfirmationDialog, @@ -33,6 +38,7 @@ import "../../components/hui-entity-editor"; import { DEFAULT_VIEW_LAYOUT, PANEL_VIEW_LAYOUT, + SECTION_VIEW_LAYOUT, VIEWS_NO_BADGE_SUPPORT, } from "../../views/const"; import { addView, deleteView, replaceView } from "../config-util"; @@ -46,12 +52,6 @@ import { import "./hui-view-editor"; import "./hui-view-visibility-editor"; import { EditViewDialogParams } from "./show-edit-view-dialog"; -import { - LovelaceViewConfig, - isStrategyView, -} from "../../../../data/lovelace/config/view"; -import { LovelaceBadgeConfig } from "../../../../data/lovelace/config/badge"; -import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; @customElement("hui-dialog-edit-view") export class HuiDialogEditView extends LitElement { @@ -63,8 +63,6 @@ export class HuiDialogEditView extends LitElement { @state() private _badges?: LovelaceBadgeConfig[]; - @state() private _cards?: LovelaceCardConfig[]; - @state() private _saving = false; @state() private _curTab?: string; @@ -102,7 +100,6 @@ export class HuiDialogEditView extends LitElement { if (this._params.viewIndex === undefined) { this._config = {}; this._badges = []; - this._cards = []; this._dirty = false; return; } @@ -112,13 +109,11 @@ export class HuiDialogEditView extends LitElement { const { strategy, ...viewConfig } = view; this._config = viewConfig; this._badges = []; - this._cards = []; return; } - const { cards, badges, ...viewConfig } = view; + const { badges, ...viewConfig } = view; this._config = viewConfig; this._badges = badges ? processEditorEntities(badges) : []; - this._cards = cards; } public closeDialog(): void { @@ -181,7 +176,7 @@ export class HuiDialogEditView extends LitElement { )} ` - : ""} + : nothing}
    ${this._badges.map( (badgeConfig) => html` @@ -193,7 +188,7 @@ export class HuiDialogEditView extends LitElement { )}
    ` - : ""} + : nothing} ` - : ""} + : nothing} ${content} ${this._params.viewIndex !== undefined @@ -312,7 +307,7 @@ export class HuiDialogEditView extends LitElement { )} ` - : ""} + : nothing} ` - : ""} + : nothing} ${this.hass!.localize("ui.common.save")} @@ -361,24 +356,28 @@ export class HuiDialogEditView extends LitElement { } } - private _deleteConfirm(): void { - showConfirmationDialog(this, { - title: this.hass!.localize( - `ui.panel.lovelace.views.confirm_delete${ - this._cards?.length ? `_existing_cards` : "" - }` - ), + private async _deleteConfirm() { + const type = this._config?.sections?.length + ? "sections" + : this._config?.cards?.length + ? "cards" + : "only"; + + const named = this._config?.title ? "named" : "unnamed"; + + const confirm = await showConfirmationDialog(this, { + title: this.hass!.localize("ui.panel.lovelace.views.delete_title"), text: this.hass!.localize( - `ui.panel.lovelace.views.confirm_delete${ - this._cards?.length ? "_existing_cards" : "" - }_text`, - { - name: this._config?.title || "Unnamed view", - number: this._cards?.length || 0, - } + `ui.panel.lovelace.views.delete_${named}_view_${type}`, + { name: this._config?.title } ), - confirm: () => this._delete(), + confirmText: this.hass!.localize("ui.common.delete"), + destructive: true, }); + + if (!confirm) return; + + this._delete(); } private _handleTabSelected(ev: CustomEvent): void { @@ -402,9 +401,18 @@ export class HuiDialogEditView extends LitElement { const viewConf: LovelaceViewConfig = { ...this._config, badges: this._badges, - cards: this._cards, }; + if (viewConf.type === SECTION_VIEW_LAYOUT && !viewConf.sections?.length) { + viewConf.sections = [{ cards: [] }]; + } else if (!viewConf.cards?.length) { + viewConf.cards = []; + } + + if (!viewConf.badges?.length) { + delete viewConf.badges; + } + const lovelace = this._params.lovelace!; try { @@ -469,7 +477,7 @@ export class HuiDialogEditView extends LitElement { if (!ev.detail.isValid) { return; } - const { badges = [], ...config } = ev.detail.value; + const { badges, ...config } = ev.detail.value; this._config = config; this._badges = badges; this._dirty = true; diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts index 6e80a6266f..fcd430c8ea 100644 --- a/src/panels/lovelace/views/hui-sections-view.ts +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -180,38 +180,21 @@ export class SectionsView extends LitElement implements LovelaceViewElement { path ) as LovelaceRawSectionConfig; - const title = section.title; + const title = section.title?.trim(); const cardCount = section.cards?.length; if (title || cardCount) { - const sectionName = title?.trim() - ? this.hass.localize( - "ui.panel.lovelace.editor.delete_section.named_section", - { name: title } - ) - : this.hass.localize( - "ui.panel.lovelace.editor.delete_section.unnamed_section" - ); - - const content = cardCount - ? this.hass.localize( - "ui.panel.lovelace.editor.delete_section.text_section_and_cards", - { - section: sectionName, - } - ) - : this.hass.localize( - "ui.panel.lovelace.editor.delete_section.text_section_only", - { - section: sectionName, - } - ); + const named = title ? "named" : "unnamed"; + const type = cardCount ? "cards" : "only"; const confirm = await showConfirmationDialog(this, { title: this.hass.localize( "ui.panel.lovelace.editor.delete_section.title" ), - text: content, + text: this.hass.localize( + `ui.panel.lovelace.editor.delete_section.text_${named}_section_${type}`, + { name: title } + ), confirmText: this.hass.localize("ui.common.delete"), destructive: true, }); diff --git a/src/translations/en.json b/src/translations/en.json index 1ad7fed83a..8e9d1f97b5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5026,10 +5026,13 @@ "saving_failed": "Saving dashboard configuration failed." }, "views": { - "confirm_delete": "Delete view?", - "confirm_delete_text": "Are you sure you want to delete your ''{name}'' view?", - "confirm_delete_existing_cards": "Deleting this view will also remove the cards", - "confirm_delete_existing_cards_text": "Are you sure you want to delete your ''{name}'' view? The view contains {number} cards that will be deleted. This action cannot be undone." + "delete_title": "Delete view", + "delete_named_view_only": "''{name}'' view will be deleted.", + "delete_unnamed_view_only": "This view will be deleted.", + "delete_named_view_cards": "''{name}'' view and all its cards will be deleted.", + "delete_unnamed_view_cards": "This view and all its cards will be deleted.", + "delete_named_view_sections": "''{name}'' view and all its sections will be deleted.", + "delete_unnamed_view_sections": "This view and all its sections will be deleted." }, "menu": { "configure_ui": "Edit dashboard", @@ -5162,10 +5165,10 @@ }, "delete_section": { "title": "Delete section", - "named_section": "\"{name}\" section", - "unnamed_section": "This section", - "text_section_only": "{section} will be deleted.", - "text_section_and_cards": "{section} and all its cards will be deleted." + "text_named_section_only": "''{name}'' section will be deleted.", + "text_unnamed_section_only": "This section will be deleted.", + "text_named_section_cards": "''{name}'' section and all its cards will be deleted.", + "text_unnamed_section_cards": "This section and all its cards will be deleted." }, "edit_section_title": { "title": "Edit name", From ccdd906e2f167afd8fce1fed7a472e96e853b7f7 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 28 Feb 2024 05:37:02 -0500 Subject: [PATCH 092/116] Restore alphabetical sorting to Areas (#19897) --- src/data/ws-area_registry.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/data/ws-area_registry.ts b/src/data/ws-area_registry.ts index 43e3d1f13f..550b9a5db8 100644 --- a/src/data/ws-area_registry.ts +++ b/src/data/ws-area_registry.ts @@ -1,12 +1,17 @@ import { Connection, createCollection } from "home-assistant-js-websocket"; import { Store } from "home-assistant-js-websocket/dist/store"; +import { stringCompare } from "../common/string/compare"; import { debounce } from "../common/util/debounce"; import { AreaRegistryEntry } from "./area_registry"; const fetchAreaRegistry = (conn: Connection) => - conn.sendMessagePromise({ - type: "config/area_registry/list", - }); + conn + .sendMessagePromise({ + type: "config/area_registry/list", + }) + .then((areas) => + areas.sort((ent1, ent2) => stringCompare(ent1.name, ent2.name)) + ); const subscribeAreaRegistryUpdates = ( conn: Connection, From 83190c21dbec3208021e85a03479409f7f86ec4e Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 28 Feb 2024 11:42:07 +0100 Subject: [PATCH 093/116] Clear Matter ping result when closing the dialog (#19901) --- .../integration-panels/matter/dialog-matter-ping-node.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts b/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts index 77ec91ee24..cbe8dc544f 100644 --- a/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts +++ b/src/panels/config/integrations/integration-panels/matter/dialog-matter-ping-node.ts @@ -137,6 +137,7 @@ class DialogMatterPingNode extends LitElement { public closeDialog(): void { this.device_id = undefined; this._status = undefined; + this._pingResult = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } From 401bbed67b8dfcd3d921119c8e41836e982f06fe Mon Sep 17 00:00:00 2001 From: Jim Date: Wed, 28 Feb 2024 10:42:19 +0000 Subject: [PATCH 094/116] Improve service filtering (#19811) * Improve service filtering Split filter term by space and match if each word is individually present * Prettier formatting * Fix un-necessary toLowerCase() call Co-authored-by: karwosts <32912880+karwosts@users.noreply.github.com> * Combine filter check conditions into the same loop * Prettier formatting --------- Co-authored-by: karwosts <32912880+karwosts@users.noreply.github.com> --- src/components/ha-service-picker.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/ha-service-picker.ts b/src/components/ha-service-picker.ts index c370928521..c27c039a8e 100644 --- a/src/components/ha-service-picker.ts +++ b/src/components/ha-service-picker.ts @@ -114,11 +114,14 @@ class HaServicePicker extends LitElement { if (!filter) { return processedServices; } - return processedServices.filter( - (service) => - service.service.toLowerCase().includes(filter) || - service.name?.toLowerCase().includes(filter) - ); + const split_filter = filter.split(" "); + return processedServices.filter((service) => { + const lower_service_name = service.name.toLowerCase(); + const lower_service = service.service.toLowerCase(); + return split_filter.every( + (f) => lower_service_name.includes(f) || lower_service.includes(f) + ); + }); } ); From 9ae234a02fcc9b311be1dabd30454f32c27e2e13 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Feb 2024 05:46:27 -0500 Subject: [PATCH 095/116] Mark paste button list item as interactive (#19898) * Mark paste button list item as interactive * Update add-automation-element-dialog.ts --------- Co-authored-by: Bram Kragten --- src/panels/config/automation/add-automation-element-dialog.ts | 4 +++- 1 file changed, 3 insertions(+), 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 4145a3397b..10249c8592 100644 --- a/src/panels/config/automation/add-automation-element-dialog.ts +++ b/src/panels/config/automation/add-automation-element-dialog.ts @@ -536,6 +536,8 @@ class DialogAddAutomationElement extends LitElement implements HassDialog { (!this._group || items.find((item) => item.key === this._params!.clipboardItem)) ? html`${this.hass.localize( // @ts-ignore `ui.panel.config.automation.editor.${this._params.type}s.type.${this._params.clipboardItem}.label` From 220da516063c0d4a0832a21b9b0542e7a4ebc0ff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:47:54 +0100 Subject: [PATCH 096/116] Update dependency @material/web to v1.3.0 (#19877) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 207dca4cb6..d0d477ecdf 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@material/mwc-top-app-bar": "0.27.0", "@material/mwc-top-app-bar-fixed": "0.27.0", "@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0", - "@material/web": "=1.2.0", + "@material/web": "=1.3.0", "@mdi/js": "7.4.47", "@mdi/svg": "7.4.47", "@polymer/paper-item": "3.0.1", diff --git a/yarn.lock b/yarn.lock index d7d6e089e5..d2bdb5a44f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3141,13 +3141,13 @@ __metadata: languageName: node linkType: hard -"@material/web@npm:=1.2.0": - version: 1.2.0 - resolution: "@material/web@npm:1.2.0" +"@material/web@npm:=1.3.0": + version: 1.3.0 + resolution: "@material/web@npm:1.3.0" dependencies: lit: "npm:^2.7.4 || ^3.0.0" tslib: "npm:^2.4.0" - checksum: 10/abf79ee6e247b21f046bf099accdb3439d43586f3be948b5f3fc09ab2b47a3a2b37fcaff01dde4205a9e40a3d22312cf314887f87fe930af755c4dc440a32529 + checksum: 10/7c3427747eccb69628807bed115dca96bb4a8e360e5cb97170a42ab7d469d17f0c925defa417a212ef3237c252f0b06dac9499b56c42945214f9bdeb7f7e5a0b languageName: node linkType: hard @@ -9584,7 +9584,7 @@ __metadata: "@material/mwc-top-app-bar": "npm:0.27.0" "@material/mwc-top-app-bar-fixed": "npm:0.27.0" "@material/top-app-bar": "npm:=14.0.0-canary.53b3cad2f.0" - "@material/web": "npm:=1.2.0" + "@material/web": "npm:=1.3.0" "@mdi/js": "npm:7.4.47" "@mdi/svg": "npm:7.4.47" "@octokit/auth-oauth-device": "npm:6.0.1" From 32c403d069567e2c47ca05885b9bcc854ddca4da Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:52:45 +0100 Subject: [PATCH 097/116] Replace more paper-items (#19707) Remove leftover paper items --- .../dialog-hassio-repositories.ts | 85 ++++++++++--------- .../config/devices/ha-config-device-page.ts | 37 +++----- .../integration-panels/zha/zha-group-page.ts | 33 ++++--- src/panels/mailbox/ha-panel-mailbox.ts | 37 ++++---- 4 files changed, 93 insertions(+), 99 deletions(-) diff --git a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts index 74ce8d5a6a..47f107a6c5 100644 --- a/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts +++ b/hassio/src/dialogs/repositories/dialog-hassio-repositories.ts @@ -1,7 +1,5 @@ import "@material/mwc-button/mwc-button"; import { mdiDelete, mdiDeleteOff } from "@mdi/js"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-item/paper-item-body"; import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; @@ -27,6 +25,8 @@ import type { HomeAssistant } from "../../../../src/types"; import { HassioRepositoryDialogParams } from "./show-dialog-repositories"; import type { HaTextField } from "../../../../src/components/ha-textfield"; import "../../../../src/components/ha-textfield"; +import "../../../../src/components/ha-list-new"; +import "../../../../src/components/ha-list-item-new"; @customElement("dialog-hassio-repositories") class HassioRepositoriesDialog extends LitElement { @@ -106,44 +106,46 @@ class HassioRepositoriesDialog extends LitElement { ? html`${this._error}` : ""}
    - ${repositories.length - ? repositories.map( - (repo) => html` - - -
    ${repo.name}
    -
    ${repo.maintainer}
    -
    ${repo.url}
    -
    -
    - - - - ${this._dialogParams!.supervisor.localize( - usedRepositories.includes(repo.slug) - ? "dialog.repositories.used" - : "dialog.repositories.remove" - )} - -
    -
    - ` - ) - : html` No repositories `} + + ${repositories.length + ? repositories.map( + (repo) => html` + + ${repo.name} +
    +
    ${repo.maintainer}
    +
    ${repo.url}
    +
    +
    + + + + ${this._dialogParams!.supervisor.localize( + usedRepositories.includes(repo.slug) + ? "dialog.repositories.used" + : "dialog.repositories.remove" + )} + +
    +
    + ` + ) + : html` No repositories `} +
    - - - ${computeStateName(entityState)} - - - + ${computeStateName(entityState)} + + ${!entityState.attributes.id ? html` @@ -528,15 +527,14 @@ export class HaConfigDevicePage extends LitElement { : undefined )} > - - - ${computeStateName(entityState)} - - - + ${computeStateName(entityState)} + + ${!entityState.attributes.id ? html` @@ -623,12 +621,10 @@ export class HaConfigDevicePage extends LitElement { return entityState ? html` - - - ${computeStateName(entityState)} - - - + + ${computeStateName(entityState)} + + ` : ""; @@ -1518,11 +1514,6 @@ export class HaConfigDevicePage extends LitElement { margin-top: 0; } - paper-item { - cursor: pointer; - font-size: var(--paper-font-body1_-_font-size); - } - a { text-decoration: none; color: var(--primary-color); diff --git a/src/panels/config/integrations/integration-panels/zha/zha-group-page.ts b/src/panels/config/integrations/integration-panels/zha/zha-group-page.ts index f21f688e07..0216d0a0df 100644 --- a/src/panels/config/integrations/integration-panels/zha/zha-group-page.ts +++ b/src/panels/config/integrations/integration-panels/zha/zha-group-page.ts @@ -1,6 +1,5 @@ import "@material/mwc-button"; import { mdiDelete } from "@mdi/js"; -import "@polymer/paper-item/paper-item"; import { CSSResultGroup, LitElement, @@ -32,6 +31,8 @@ import "../../../ha-config-section"; import { formatAsPaddedHex } from "./functions"; import "./zha-device-endpoint-data-table"; import type { ZHADeviceEndpointDataTable } from "./zha-device-endpoint-data-table"; +import "@material/mwc-list/mwc-list"; +import "../../../../../components/ha-list-item"; @customElement("zha-group-page") export class ZHAGroupPage extends LitElement { @@ -131,20 +132,24 @@ export class ZHAGroupPage extends LitElement { ${this.hass.localize("ui.panel.config.zha.groups.members")}
    - ${this.group.members.length - ? this.group.members.map( - (member) => - html` - ${member.device.user_given_name || - member.device.name} + ${this.group.members.length + ? this.group.members.map( + (member) => + html` - ` - ) - : html` This group has no members `} + ${member.device.user_given_name || + member.device.name} + ` + ) + : html` + This group has no members + `} + ${this.group.members.length ? html` diff --git a/src/panels/mailbox/ha-panel-mailbox.ts b/src/panels/mailbox/ha-panel-mailbox.ts index 643112c7bd..250af9a83a 100644 --- a/src/panels/mailbox/ha-panel-mailbox.ts +++ b/src/panels/mailbox/ha-panel-mailbox.ts @@ -12,8 +12,6 @@ import { formatDateTime } from "../../common/datetime/format_date_time"; import "../../components/ha-card"; import "../../components/ha-menu-button"; import "../../components/ha-tabs"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-tabs/paper-tab"; import { HomeAssistant } from "../../types"; import { fireEvent } from "../../common/dom/fire_event"; @@ -82,25 +80,24 @@ class HaPanelMailbox extends LitElement { : nothing} ${this._messages?.map( (message) => - html` - -
    -
    ${message.caller}
    -
    - ${formatDuration(this.hass.locale, { - seconds: message.duration, - })} -
    -
    -
    - ${message.timestamp} - - ${message.message} -
    -
    -
    ` + + ${message.caller} + + ${formatDuration(this.hass.locale, { + seconds: message.duration, + })} + + + + ${message.timestamp} - + ${message.message} + + ` )}
    @@ -219,10 +216,6 @@ class HaPanelMailbox extends LitElement { overflow: hidden; } - paper-item { - cursor: pointer; - } - ha-tabs { margin-left: max(env(safe-area-inset-left), 24px); margin-right: max(env(safe-area-inset-right), 24px); From d5f8231f9798eaa61b261f1857c46e8ca5636b72 Mon Sep 17 00:00:00 2001 From: Cody C <50791984+codyc1515@users.noreply.github.com> Date: Wed, 28 Feb 2024 23:57:12 +1300 Subject: [PATCH 098/116] Update dashboard resources title to reflect translations (#19641) * Update dashboard resources title to reflect translations * Update dashboard resources title to reflect translations --- src/panels/config/lovelace/ha-config-lovelace.ts | 9 ++++++++- .../lovelace/resources/ha-config-lovelace-resources.ts | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/panels/config/lovelace/ha-config-lovelace.ts b/src/panels/config/lovelace/ha-config-lovelace.ts index c494232bb8..eda9a8fd56 100644 --- a/src/panels/config/lovelace/ha-config-lovelace.ts +++ b/src/panels/config/lovelace/ha-config-lovelace.ts @@ -8,13 +8,20 @@ import { HomeAssistant } from "../../../types"; export const lovelaceTabs = [ { - component: "lovelace", path: "/config/lovelace/dashboards", translationKey: "ui.panel.config.lovelace.dashboards.caption", iconPath: mdiViewDashboard, }, ]; +export const lovelaceResourcesTabs = [ + { + path: "/config/lovelace/resources", + translationKey: "ui.panel.config.lovelace.resources.caption", + iconPath: mdiViewDashboard, + }, +]; + @customElement("ha-config-lovelace") class HaConfigLovelace extends HassRouterPage { @property({ attribute: false }) public hass!: HomeAssistant; diff --git a/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts b/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts index 9574f14fce..7713108edd 100644 --- a/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts +++ b/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts @@ -34,7 +34,7 @@ import "../../../../layouts/hass-tabs-subpage-data-table"; import { haStyle } from "../../../../resources/styles"; import { HomeAssistant, Route } from "../../../../types"; import { loadLovelaceResources } from "../../../lovelace/common/load-resources"; -import { lovelaceTabs } from "../ha-config-lovelace"; +import { lovelaceResourcesTabs } from "../ha-config-lovelace"; import { showResourceDetailDialog } from "./show-dialog-lovelace-resource-detail"; @customElement("ha-config-lovelace-resources") @@ -117,7 +117,7 @@ export class HaConfigLovelaceRescources extends LitElement { .hass=${this.hass} .narrow=${this.narrow} .route=${this.route} - .tabs=${lovelaceTabs} + .tabs=${lovelaceResourcesTabs} .columns=${this._columns(this.hass.language)} .data=${this._resources} .noDataText=${this.hass.localize( From 4db743db00d66a4315f1b50384c83c17e79f5d83 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 12:07:27 +0100 Subject: [PATCH 099/116] Update dependency webpack-dev-server to v5 (#19807) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 151 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 92 insertions(+), 61 deletions(-) diff --git a/package.json b/package.json index d0d477ecdf..60688e33b7 100644 --- a/package.json +++ b/package.json @@ -242,7 +242,7 @@ "vinyl-source-stream": "2.0.0", "webpack": "5.90.3", "webpack-cli": "5.1.4", - "webpack-dev-server": "4.15.1", + "webpack-dev-server": "5.0.2", "webpack-manifest-plugin": "5.0.0", "webpack-stats-plugin": "1.1.3", "webpackbar": "6.0.1", diff --git a/yarn.lock b/yarn.lock index d2bdb5a44f..1a0ff9d448 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3968,7 +3968,7 @@ __metadata: languageName: node linkType: hard -"@types/bonjour@npm:^3.5.9": +"@types/bonjour@npm:^3.5.13": version: 3.5.13 resolution: "@types/bonjour@npm:3.5.13" dependencies: @@ -4010,7 +4010,7 @@ __metadata: languageName: node linkType: hard -"@types/connect-history-api-fallback@npm:^1.3.5": +"@types/connect-history-api-fallback@npm:^1.5.4": version: 1.5.4 resolution: "@types/connect-history-api-fallback@npm:1.5.4" dependencies: @@ -4094,7 +4094,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.17.13": +"@types/express@npm:*, @types/express@npm:^4.17.21": version: 4.17.21 resolution: "@types/express@npm:4.17.21" dependencies: @@ -4406,10 +4406,10 @@ __metadata: languageName: node linkType: hard -"@types/retry@npm:0.12.0": - version: 0.12.0 - resolution: "@types/retry@npm:0.12.0" - checksum: 10/bbd0b88f4b3eba7b7acfc55ed09c65ef6f2e1bcb4ec9b4dca82c66566934351534317d294a770a7cc6c0468d5573c5350abab6e37c65f8ef254443e1b028e44d +"@types/retry@npm:0.12.2": + version: 0.12.2 + resolution: "@types/retry@npm:0.12.2" + checksum: 10/e5675035717b39ce4f42f339657cae9637cf0c0051cf54314a6a2c44d38d91f6544be9ddc0280587789b6afd056be5d99dbe3e9f4df68c286c36321579b1bf4a languageName: node linkType: hard @@ -4439,7 +4439,7 @@ __metadata: languageName: node linkType: hard -"@types/serve-index@npm:^1.9.1": +"@types/serve-index@npm:^1.9.4": version: 1.9.4 resolution: "@types/serve-index@npm:1.9.4" dependencies: @@ -4448,7 +4448,7 @@ __metadata: languageName: node linkType: hard -"@types/serve-static@npm:*, @types/serve-static@npm:^1.13.10": +"@types/serve-static@npm:*, @types/serve-static@npm:^1.15.5": version: 1.15.5 resolution: "@types/serve-static@npm:1.15.5" dependencies: @@ -4459,7 +4459,7 @@ __metadata: languageName: node linkType: hard -"@types/sockjs@npm:^0.3.33": +"@types/sockjs@npm:^0.3.36": version: 0.3.36 resolution: "@types/sockjs@npm:0.3.36" dependencies: @@ -4515,7 +4515,7 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.5": +"@types/ws@npm:^8.5.10": version: 8.5.10 resolution: "@types/ws@npm:8.5.10" dependencies: @@ -6136,7 +6136,7 @@ __metadata: languageName: node linkType: hard -"bonjour-service@npm:^1.0.11": +"bonjour-service@npm:^1.2.1": version: 1.2.1 resolution: "bonjour-service@npm:1.2.1" dependencies: @@ -6501,7 +6501,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.4.3, chokidar@npm:^3.5.3": +"chokidar@npm:^3.4.3, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -9116,7 +9116,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:10.3.10, glob@npm:^10.2.2, glob@npm:^10.3.10": +"glob@npm:10.3.10, glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": version: 10.3.10 resolution: "glob@npm:10.3.10" dependencies: @@ -9727,7 +9727,7 @@ __metadata: vue2-daterange-picker: "npm:0.6.8" webpack: "npm:5.90.3" webpack-cli: "npm:5.1.4" - webpack-dev-server: "npm:4.15.1" + webpack-dev-server: "npm:5.0.2" webpack-manifest-plugin: "npm:5.0.0" webpack-stats-plugin: "npm:1.1.3" webpackbar: "npm:6.0.1" @@ -9778,7 +9778,7 @@ __metadata: languageName: node linkType: hard -"html-entities@npm:^2.3.2": +"html-entities@npm:^2.4.0": version: 2.4.0 resolution: "html-entities@npm:2.4.0" checksum: 10/646f2f19214bad751e060ceef4df98520654a1d0cd631b55d45504df2f0aaf8a14d8c0a5a4f92b353be298774d856157ac2d04a031d78889c9011892078ca157 @@ -10181,7 +10181,7 @@ __metadata: languageName: node linkType: hard -"ipaddr.js@npm:^2.0.1": +"ipaddr.js@npm:^2.1.0": version: 2.1.0 resolution: "ipaddr.js@npm:2.1.0" checksum: 10/42c16d95cf451399707c2c46e605b88db1ea2b1477b25774b5a7ee96852b0bb1efdc01adbff01fedbe702ff246e1aca5c5e915a6f5a1f1485233a5f7c2eb73c2 @@ -10477,6 +10477,13 @@ __metadata: languageName: node linkType: hard +"is-network-error@npm:^1.0.0": + version: 1.0.1 + resolution: "is-network-error@npm:1.0.1" + checksum: 10/165d61500c4186c62db5a3a693d6bfa14ca40fe9b471ef4cd4f27b20ef6760880faf5386dc01ca9867531631782941fedaa94521d09959edf71f046e393c7b91 + languageName: node + linkType: hard + "is-number-object@npm:^1.0.4": version: 1.0.7 resolution: "is-number-object@npm:1.0.7" @@ -11158,7 +11165,7 @@ __metadata: languageName: node linkType: hard -"launch-editor@npm:^2.6.0": +"launch-editor@npm:^2.6.1": version: 2.6.1 resolution: "launch-editor@npm:2.6.1" dependencies: @@ -11691,7 +11698,7 @@ __metadata: languageName: node linkType: hard -"memfs@npm:^3.4.3, memfs@npm:^3.5.0": +"memfs@npm:^3.5.0": version: 3.5.3 resolution: "memfs@npm:3.5.3" dependencies: @@ -11700,6 +11707,15 @@ __metadata: languageName: node linkType: hard +"memfs@npm:^4.6.0": + version: 4.7.6 + resolution: "memfs@npm:4.7.6" + dependencies: + tslib: "npm:^2.0.0" + checksum: 10/b2a4c95635aa7ed9162c647b60ebbe6f84c541dd91d99e56602f1e904503447aaf92c46918ddf0b84acfb8e52e9af90bd63a6c0cfb1cc8bfdf31379f2fba8e11 + languageName: node + linkType: hard + "memoize-one@npm:6.0.0": version: 6.0.0 resolution: "memoize-one@npm:6.0.0" @@ -12523,7 +12539,7 @@ __metadata: languageName: node linkType: hard -"open@npm:10.0.3": +"open@npm:10.0.3, open@npm:^10.0.3": version: 10.0.3 resolution: "open@npm:10.0.3" dependencies: @@ -12535,7 +12551,7 @@ __metadata: languageName: node linkType: hard -"open@npm:^8.0.2, open@npm:^8.0.9, open@npm:^8.4.0": +"open@npm:^8.0.2, open@npm:^8.4.0": version: 8.4.2 resolution: "open@npm:8.4.2" dependencies: @@ -12650,13 +12666,14 @@ __metadata: languageName: node linkType: hard -"p-retry@npm:^4.5.0": - version: 4.6.2 - resolution: "p-retry@npm:4.6.2" +"p-retry@npm:^6.2.0": + version: 6.2.0 + resolution: "p-retry@npm:6.2.0" dependencies: - "@types/retry": "npm:0.12.0" + "@types/retry": "npm:0.12.2" + is-network-error: "npm:^1.0.0" retry: "npm:^0.13.1" - checksum: 10/45c270bfddaffb4a895cea16cb760dcc72bdecb6cb45fef1971fa6ea2e91ddeafddefe01e444ac73e33b1b3d5d29fb0dd18a7effb294262437221ddc03ce0f2e + checksum: 10/1a5ac16828c96c03c354f78d643dfc7aa8f8b998e1b60e27533da2c75e5cabfb1c7f88ce312e813e09a80b056011fbb372d384132e9c92d27d052bd7c282a978 languageName: node linkType: hard @@ -13799,6 +13816,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^5.0.5": + version: 5.0.5 + resolution: "rimraf@npm:5.0.5" + dependencies: + glob: "npm:^10.3.7" + bin: + rimraf: dist/esm/bin.mjs + checksum: 10/a612c7184f96258b7d1328c486b12ca7b60aa30e04229a08bbfa7e964486deb1e9a1b52d917809311bdc39a808a4055c0f950c0280fba194ba0a09e6f0d404f6 + languageName: node + linkType: hard + "roboto-fontface@npm:0.10.0": version: 0.10.0 resolution: "roboto-fontface@npm:0.10.0" @@ -13967,7 +13995,7 @@ __metadata: languageName: node linkType: hard -"schema-utils@npm:^4.0.0": +"schema-utils@npm:^4.0.0, schema-utils@npm:^4.2.0": version: 4.2.0 resolution: "schema-utils@npm:4.2.0" dependencies: @@ -13986,7 +14014,7 @@ __metadata: languageName: node linkType: hard -"selfsigned@npm:^2.1.1": +"selfsigned@npm:^2.4.1": version: 2.4.1 resolution: "selfsigned@npm:2.4.1" dependencies: @@ -15299,7 +15327,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.0.2, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.2, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 10/bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca @@ -16137,57 +16165,60 @@ __metadata: languageName: node linkType: hard -"webpack-dev-middleware@npm:^5.3.1": - version: 5.3.3 - resolution: "webpack-dev-middleware@npm:5.3.3" +"webpack-dev-middleware@npm:^7.0.0": + version: 7.0.0 + resolution: "webpack-dev-middleware@npm:7.0.0" dependencies: colorette: "npm:^2.0.10" - memfs: "npm:^3.4.3" + memfs: "npm:^4.6.0" mime-types: "npm:^2.1.31" range-parser: "npm:^1.2.1" schema-utils: "npm:^4.0.0" peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - checksum: 10/31a2f7a11e58a76bdcde1eb8da310b6643844d9b442f9916f48be5b46c103f23490c393c32a9af501ce68226fbb018b811f5a956635ed60a03f9481a4bcd6c76 + webpack: ^5.0.0 + peerDependenciesMeta: + webpack: + optional: true + checksum: 10/e94902c35458c2431cd3332088cf33afe9236333e911605e6045392be11f521140600312c698263acf2a6c52e2092b8e8b3ea4b0ed34568df6cb7a79913aa1d3 languageName: node linkType: hard -"webpack-dev-server@npm:4.15.1": - version: 4.15.1 - resolution: "webpack-dev-server@npm:4.15.1" +"webpack-dev-server@npm:5.0.2": + version: 5.0.2 + resolution: "webpack-dev-server@npm:5.0.2" dependencies: - "@types/bonjour": "npm:^3.5.9" - "@types/connect-history-api-fallback": "npm:^1.3.5" - "@types/express": "npm:^4.17.13" - "@types/serve-index": "npm:^1.9.1" - "@types/serve-static": "npm:^1.13.10" - "@types/sockjs": "npm:^0.3.33" - "@types/ws": "npm:^8.5.5" + "@types/bonjour": "npm:^3.5.13" + "@types/connect-history-api-fallback": "npm:^1.5.4" + "@types/express": "npm:^4.17.21" + "@types/serve-index": "npm:^1.9.4" + "@types/serve-static": "npm:^1.15.5" + "@types/sockjs": "npm:^0.3.36" + "@types/ws": "npm:^8.5.10" ansi-html-community: "npm:^0.0.8" - bonjour-service: "npm:^1.0.11" - chokidar: "npm:^3.5.3" + bonjour-service: "npm:^1.2.1" + chokidar: "npm:^3.6.0" colorette: "npm:^2.0.10" compression: "npm:^1.7.4" connect-history-api-fallback: "npm:^2.0.0" default-gateway: "npm:^6.0.3" express: "npm:^4.17.3" graceful-fs: "npm:^4.2.6" - html-entities: "npm:^2.3.2" + html-entities: "npm:^2.4.0" http-proxy-middleware: "npm:^2.0.3" - ipaddr.js: "npm:^2.0.1" - launch-editor: "npm:^2.6.0" - open: "npm:^8.0.9" - p-retry: "npm:^4.5.0" - rimraf: "npm:^3.0.2" - schema-utils: "npm:^4.0.0" - selfsigned: "npm:^2.1.1" + ipaddr.js: "npm:^2.1.0" + launch-editor: "npm:^2.6.1" + open: "npm:^10.0.3" + p-retry: "npm:^6.2.0" + rimraf: "npm:^5.0.5" + schema-utils: "npm:^4.2.0" + selfsigned: "npm:^2.4.1" serve-index: "npm:^1.9.1" sockjs: "npm:^0.3.24" spdy: "npm:^4.0.2" - webpack-dev-middleware: "npm:^5.3.1" - ws: "npm:^8.13.0" + webpack-dev-middleware: "npm:^7.0.0" + ws: "npm:^8.16.0" peerDependencies: - webpack: ^4.37.0 || ^5.0.0 + webpack: ^5.0.0 peerDependenciesMeta: webpack: optional: true @@ -16195,7 +16226,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: 10/fd6dfb6c71eb94696b21930ea4c2f25e95ba85fac1bbc15aa5d03af0a90712eba057901fa9131ed3e901665c95b2379208279aca61e9c48e7cda276c3caa95dd + checksum: 10/f47205b56a562c72083ad979fceb499dc60ef35a75a72b6fbcbccd258b6b304ab3a977877dc6ce68aa5fb90cee8ab9387e22ceb8e374a019e3d4ce77ad0c9493 languageName: node linkType: hard @@ -16714,7 +16745,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.13.0": +"ws@npm:^8.16.0": version: 8.16.0 resolution: "ws@npm:8.16.0" peerDependencies: From 1d9fa1522cd6084688fb96dd81acdc8764f58664 Mon Sep 17 00:00:00 2001 From: Cody C <50791984+codyc1515@users.noreply.github.com> Date: Thu, 29 Feb 2024 00:07:38 +1300 Subject: [PATCH 100/116] Fix MFA module name not showing on step completion (#19581) * Fix MFA module name not showing on step completion Fixes #18918 * Update src/panels/profile/dialog-ha-mfa-module-setup-flow.ts --------- Co-authored-by: Bram Kragten --- src/panels/profile/dialog-ha-mfa-module-setup-flow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/profile/dialog-ha-mfa-module-setup-flow.ts b/src/panels/profile/dialog-ha-mfa-module-setup-flow.ts index 0ca1b026e6..c7bad0fa14 100644 --- a/src/panels/profile/dialog-ha-mfa-module-setup-flow.ts +++ b/src/panels/profile/dialog-ha-mfa-module-setup-flow.ts @@ -97,7 +97,7 @@ class HaMfaModuleSetupFlow extends LitElement { ? html`

    ${this.hass.localize( "ui.panel.profile.mfa_setup.step_done", - { step: this._step.title } + { step: this._step.title || this._step.handler } )}

    ` : this._step.type === "form" From 3b89b72568c9a66a40e758ed43b4502052e52030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Dimja=C5=A1evi=C4=87?= Date: Wed, 28 Feb 2024 12:29:18 +0100 Subject: [PATCH 101/116] Automation editor: clarify multiple triggers logic (#19647) * Automation editor: clarify multiple triggers logic * Implement PR feedback * Update src/translations/en.json --------- Co-authored-by: Bram Kragten --- src/translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translations/en.json b/src/translations/en.json index 8e9d1f97b5..99c3f234f6 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2507,7 +2507,7 @@ "triggers": { "name": "Triggers", "header": "When", - "description": "This list of triggers is what starts your automation. A trigger is a specific event happening in or around your home, for example: ''When the sun sets''.", + "description": "A trigger is a specific event happening in or around your home, for example: ''When the sun sets''. Any trigger listed here will start your automation.", "learn_more": "Learn more about triggers", "triggered": "Triggered", "add": "Add trigger", From 5b3074d9392839b436b676ec027868b982ec5d6e Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 28 Feb 2024 12:33:21 +0100 Subject: [PATCH 102/116] Replace button to manually add a Thread border router with more info button (#19903) Replace button to manually add a border router with more info button --- .../thread/thread-config-panel.ts | 14 ++++++++++---- src/translations/en.json | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts b/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts index e8e068ca22..7a5375d8b2 100644 --- a/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts @@ -54,6 +54,7 @@ import { haStyle } from "../../../../../resources/styles"; import { HomeAssistant } from "../../../../../types"; import { brandsUrl } from "../../../../../util/brands-url"; import { fileDownload } from "../../../../../util/file_download"; +import { documentationUrl } from "../../../../../util/documentation-url"; interface ThreadNetwork { name: string; @@ -123,11 +124,16 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { )} - ${this.hass.localize( - "ui.panel.config.thread.add_open_thread_border_router" - )} + ${this.hass.localize( + "ui.panel.config.thread.more_info" + )} +
    `} ${networks.networks.length diff --git a/src/translations/en.json b/src/translations/en.json index 99c3f234f6..499a495fda 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4127,6 +4127,7 @@ "other_networks": "Other networks", "my_network": "Preferred network", "no_preferred_network": "You don't have a preferred network yet.", + "more_info": "More Info", "add_open_thread_border_router": "Add an OpenThread border router", "reset_border_router": "Reset border router", "add_to_my_network": "Add to preferred network", From c945534640e0e01ca8085abfafe649ee490d5c41 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 Feb 2024 12:35:32 +0100 Subject: [PATCH 103/116] Disable the ability to change the view type to sections and the other way around (#19902) * Disable the ability to change the view type to sections and the other way around * Update src/translations/en.json Co-authored-by: Bram Kragten * Update src/translations/en.json Co-authored-by: Bram Kragten * Update src/translations/en.json Co-authored-by: Bram Kragten --------- Co-authored-by: Bram Kragten --- src/components/ha-formfield.ts | 7 +++++- .../ha-selector/ha-selector-select.ts | 5 +++- .../editor/view-editor/hui-view-editor.ts | 25 ++++++++++++++++--- src/translations/en.json | 3 +++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/components/ha-formfield.ts b/src/components/ha-formfield.ts index 1c8cc5a019..46837ca65d 100644 --- a/src/components/ha-formfield.ts +++ b/src/components/ha-formfield.ts @@ -1,11 +1,13 @@ import { FormfieldBase } from "@material/mwc-formfield/mwc-formfield-base"; import { styles } from "@material/mwc-formfield/mwc-formfield.css"; import { css } from "lit"; -import { customElement } from "lit/decorators"; +import { customElement, property } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; @customElement("ha-formfield") export class HaFormfield extends FormfieldBase { + @property({ type: Boolean, reflect: true }) public disabled = false; + protected _labelClick() { const input = this.input as HTMLInputElement | undefined; if (!input) return; @@ -44,6 +46,9 @@ export class HaFormfield extends FormfieldBase { padding-inline-start: 4px; padding-inline-end: 0; } + :host([disabled]) label { + color: var(--disabled-text-color); + } `, ]; } diff --git a/src/components/ha-selector/ha-selector-select.ts b/src/components/ha-selector/ha-selector-select.ts index 069dd157d4..91fb682d7e 100644 --- a/src/components/ha-selector/ha-selector-select.ts +++ b/src/components/ha-selector/ha-selector-select.ts @@ -102,7 +102,10 @@ export class HaSelectSelector extends LitElement { ${this.label} ${options.map( (item: SelectOption) => html` - + + (localize: LocalizeFunc, currentType: string, isNew: boolean) => [ { name: "title", selector: { text: {} } }, { @@ -61,6 +64,11 @@ export class HuiViewEditor extends LitElement { label: localize( `ui.panel.lovelace.editor.edit_view.types.${type}` ), + disabled: + !isNew && + (currentType === SECTION_VIEW_LAYOUT + ? type !== SECTION_VIEW_LAYOUT + : type === SECTION_VIEW_LAYOUT), })), }, }, @@ -71,7 +79,7 @@ export class HuiViewEditor extends LitElement { boolean: {}, }, }, - ] as const + ] as const satisfies HaFormSchema[] ); set config(config: LovelaceViewConfig) { @@ -92,7 +100,7 @@ export class HuiViewEditor extends LitElement { return nothing; } - const schema = this._schema(this.hass.localize); + const schema = this._schema(this.hass.localize, this._type, this.isNew); const data = { ...this._config, @@ -156,6 +164,15 @@ export class HuiViewEditor extends LitElement { return this.hass.localize( "ui.panel.lovelace.editor.edit_view.subview_helper" ); + case "type": + if (this.isNew) return undefined; + return this._type === "sections" + ? this.hass.localize( + "ui.panel.lovelace.editor.edit_view.type_helper_others" + ) + : this.hass.localize( + "ui.panel.lovelace.editor.edit_view.type_helper_sections" + ); default: return undefined; } diff --git a/src/translations/en.json b/src/translations/en.json index 499a495fda..fbec99072e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5101,6 +5101,9 @@ "select_users": "Select which users should see this view in the navigation" }, "type": "View type", + "type_helper_sections": "You can not change your view to use the 'sections' view type, because migration is not supported yet. Start from scratch with a new view if you want to experiment with the 'sections' view.", + "type_helper_others": "You can not change your view to an other type because migration is not supported yet. Start from scratch with a new view if you want to use another view type.", + "types": { "masonry": "Masonry (default)", "sidebar": "Sidebar", From 763c672e36a57d233b40d04168dd1c98704a738e Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 28 Feb 2024 06:54:44 -0500 Subject: [PATCH 104/116] Automated outlier detection for adjust sum dialog (#18723) --- .../dialog-statistics-adjust-sum.ts | 107 ++++++++++++++++++ src/translations/en.json | 1 + 2 files changed, 108 insertions(+) diff --git a/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts b/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts index 6ffa9790da..b32666bde6 100644 --- a/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts +++ b/src/panels/developer-tools/statistics/dialog-statistics-adjust-sum.ts @@ -34,6 +34,11 @@ import { HomeAssistant } from "../../../types"; import { showToast } from "../../../util/toast"; import type { DialogStatisticsAdjustSumParams } from "./show-dialog-statistics-adjust-sum"; +interface CombinedStat { + hour: StatisticValue | null; + fiveMin: StatisticValue[]; +} + @customElement("dialog-statistics-adjust-sum") export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -196,6 +201,13 @@ export class DialogStatisticsFixUnsupportedUnitMetadata extends LitElement { @value-changed=${this._dateTimeSelectorChanged} >
    ${stats}
    + { + this._stats5min = undefined; + this._statsHour = undefined; + const statId = this._params!.statistic.statistic_id; + + // Get all the data + const start = new Date(0); + const end = new Date(); + + const statsHourData = await fetchStatistics( + this.hass, + start, + end, + [statId], + "hour" + ); + + const statsHour = statId in statsHourData ? statsHourData[statId] : []; + if (statsHour.length === 0) { + return; + } + + const stats5MinData = await fetchStatistics( + this.hass, + start, + end, + [statId], + "5minute" + ); + + const stats5Min = statId in stats5MinData ? stats5MinData[statId] : []; + // First datapoint of 5 minute data in the history is always junk since it counts the entire sum + // as the change, which we don't want here. + stats5Min.shift(); + + const combinedStatsData: CombinedStat[] = []; + statsHour.forEach((s) => { + combinedStatsData.push({ hour: s, fiveMin: [] }); + }); + + const lasthour: CombinedStat = { hour: null, fiveMin: [] }; + + let i = 0; + stats5Min.forEach((s) => { + let matched = false; + for (i; i < combinedStatsData.length; i++) { + const hour = combinedStatsData[i].hour; + if (hour && s.start >= hour.start && s.end <= hour.end) { + combinedStatsData[i].fiveMin.push(s); + matched = true; + break; + } + } + if (!matched) { + lasthour.fiveMin.push(s); + } + }); + + combinedStatsData.push(lasthour); + + let statsOutliers: StatisticValue[] = []; + let min = 0; + const numOutliers = 10; + + // Track the top 10 values. + const addOutlier = (s) => { + const val = Math.abs(s.change ?? 0); + if (statsOutliers.length < numOutliers || val > min) { + statsOutliers.push(s); + statsOutliers = statsOutliers.sort( + (a, b) => Math.abs(b.change ?? 0) - Math.abs(a.change ?? 0) + ); + statsOutliers = statsOutliers.slice(0, numOutliers); + min = statsOutliers[statsOutliers.length - 1].change ?? 0; + } + }; + + // If an hour has no five minute data, add the hour value + // Otherwise, add the 5 minute values and ignore the hour value + combinedStatsData.forEach((c) => { + if (c.fiveMin.length === 0 && c.hour) { + addOutlier(c.hour); + } else { + c.fiveMin.forEach((s) => { + addOutlier(s); + }); + } + }); + + // Outliers are a possible mix of hour/5minute data, but the distinction + // is not relevant here, as long as only one array is populated. + this._statsHour = statsOutliers; + this._stats5min = []; + } + private async _fixIssue(): Promise { const unit = getDisplayUnit( this.hass, diff --git a/src/translations/en.json b/src/translations/en.json index fbec99072e..733d393c4c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -6355,6 +6355,7 @@ "end": "End", "new_value": "New value", "adjust": "Adjust", + "outliers": "Outliers", "sum_adjusted": "Statistic sum adjusted", "error_sum_adjusted": "Error adjusting sum: {message}" } From 291638a9ddb7629c9d0baeae9c877df936c492a7 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 28 Feb 2024 07:08:31 -0500 Subject: [PATCH 105/116] Deduplicate blueprint editor code (#19791) * Deduplicate blueprint editor code * prune unneeded imports --- .../automation/blueprint-automation-editor.ts | 266 +---------------- .../blueprint/blueprint-generic-editor.ts | 275 ++++++++++++++++++ .../config/script/blueprint-script-editor.ts | 265 +---------------- 3 files changed, 293 insertions(+), 513 deletions(-) create mode 100644 src/panels/config/blueprint/blueprint-generic-editor.ts diff --git a/src/panels/config/automation/blueprint-automation-editor.ts b/src/panels/config/automation/blueprint-automation-editor.ts index f57806f896..2ff983d4c0 100644 --- a/src/panels/config/automation/blueprint-automation-editor.ts +++ b/src/panels/config/automation/blueprint-automation-editor.ts @@ -1,56 +1,23 @@ import "@material/mwc-button/mwc-button"; import { HassEntity } from "home-assistant-js-websocket"; -import { css, CSSResultGroup, html, LitElement } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { nestedArrayMove } from "../../../common/util/array-move"; +import { html } from "lit"; +import { customElement, property } from "lit/decorators"; import "../../../components/ha-alert"; -import "../../../components/ha-blueprint-picker"; -import "../../../components/ha-card"; -import "../../../components/ha-circular-progress"; -import "../../../components/ha-markdown"; -import "../../../components/ha-selector/ha-selector"; -import "../../../components/ha-settings-row"; import { BlueprintAutomationConfig } from "../../../data/automation"; -import { - BlueprintOrError, - Blueprints, - fetchBlueprints, -} from "../../../data/blueprint"; -import { haStyle } from "../../../resources/styles"; -import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; +import { fetchBlueprints } from "../../../data/blueprint"; +import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor"; @customElement("blueprint-automation-editor") -export class HaBlueprintAutomationEditor extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property({ type: Boolean }) public isWide = false; - - @property({ type: Boolean }) public disabled = false; - - @property({ type: Boolean, reflect: true }) public narrow = false; - +export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor { @property({ attribute: false }) public config!: BlueprintAutomationConfig; @property({ attribute: false }) public stateObj?: HassEntity; - @state() private _blueprints?: Blueprints; - - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - this._getBlueprints(); - } - - private get _blueprint(): BlueprintOrError | undefined { - if (!this._blueprints) { - return undefined; - } - return this._blueprints[this.config.use_blueprint.path]; + protected get _config(): BlueprintAutomationConfig { + return this.config; } protected render() { - const blueprint = this._blueprint; return html` ${this.disabled ? html` @@ -77,167 +44,14 @@ export class HaBlueprintAutomationEditor extends LitElement { ${this.config.description ? html`

    ${this.config.description}

    ` : ""} - -
    - ${this._blueprints - ? Object.keys(this._blueprints).length - ? html` - - ` - : this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_blueprints" - ) - : html``} -
    - - ${this.config.use_blueprint.path - ? blueprint && "error" in blueprint - ? html`

    - There is an error in this Blueprint: ${blueprint.error} -

    ` - : html`${blueprint?.metadata.description - ? html`` - : ""} - ${blueprint?.metadata?.input && - Object.keys(blueprint.metadata.input).length - ? Object.entries(blueprint.metadata.input).map( - ([key, value]) => { - const selector = value?.selector ?? { text: undefined }; - const type = Object.keys(selector)[0]; - const enhancedSelector = [ - "action", - "condition", - "trigger", - ].includes(type) - ? { - [type]: { - ...selector[type], - path: [key], - }, - } - : selector; - - return html` - ${value?.name || key} - - ${html``} - `; - } - ) - : html`

    - ${this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_inputs" - )} -

    `}` - : ""} -
    + ${this.renderCard()} `; } - private async _getBlueprints() { + protected async _getBlueprints() { this._blueprints = await fetchBlueprints(this.hass, "automation"); } - private _blueprintChanged(ev) { - ev.stopPropagation(); - if (this.config.use_blueprint.path === ev.detail.value) { - return; - } - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - path: ev.detail.value, - }, - }, - }); - } - - private _inputChanged(ev) { - ev.stopPropagation(); - const target = ev.target as any; - const key = target.key; - const value = ev.detail ? ev.detail.value : target.value; - if ( - (this.config.use_blueprint.input && - this.config.use_blueprint.input[key] === value) || - (!this.config.use_blueprint.input && value === "") - ) { - return; - } - const input = { ...this.config.use_blueprint.input, [key]: value }; - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - - private _itemMoved(ev) { - ev.stopPropagation(); - const { oldIndex, newIndex, oldPath, newPath } = ev.detail; - - const input = nestedArrayMove( - this.config.use_blueprint.input, - oldIndex, - newIndex, - oldPath, - newPath - ); - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - private async _enable(): Promise { if (!this.hass || !this.stateObj) { return; @@ -246,69 +60,7 @@ export class HaBlueprintAutomationEditor extends LitElement { entity_id: this.stateObj.entity_id, }); } - - private _duplicate() { - fireEvent(this, "duplicate"); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - :host { - display: block; - } - ha-card.blueprint { - margin: 0 auto; - } - .padding { - padding: 16px; - } - .link-button-row { - padding: 14px; - } - .blueprint-picker-container { - padding: 0 16px 16px; - } - ha-textfield, - ha-blueprint-picker { - display: block; - } - h3 { - margin: 16px; - } - .introduction { - margin-top: 0; - margin-bottom: 12px; - } - .introduction a { - color: var(--primary-color); - } - p { - margin-bottom: 0; - } - .description { - margin-bottom: 16px; - } - ha-settings-row { - --paper-time-input-justify-content: flex-end; - --settings-row-content-width: 100%; - --settings-row-prefix-display: contents; - border-top: 1px solid var(--divider-color); - } - ha-alert { - margin-bottom: 16px; - display: block; - } - ha-alert.re-order { - border-radius: var(--ha-card-border-radius, 12px); - overflow: hidden; - } - `, - ]; - } } - declare global { interface HTMLElementTagNameMap { "blueprint-automation-editor": HaBlueprintAutomationEditor; diff --git a/src/panels/config/blueprint/blueprint-generic-editor.ts b/src/panels/config/blueprint/blueprint-generic-editor.ts new file mode 100644 index 0000000000..188ef35e25 --- /dev/null +++ b/src/panels/config/blueprint/blueprint-generic-editor.ts @@ -0,0 +1,275 @@ +import "@material/mwc-button/mwc-button"; +import { css, CSSResultGroup, html, LitElement } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../common/dom/fire_event"; +import { nestedArrayMove } from "../../../common/util/array-move"; +import "../../../components/ha-alert"; +import "../../../components/ha-blueprint-picker"; +import "../../../components/ha-card"; +import "../../../components/ha-circular-progress"; +import "../../../components/ha-markdown"; +import "../../../components/ha-selector/ha-selector"; +import "../../../components/ha-settings-row"; +import { BlueprintAutomationConfig } from "../../../data/automation"; +import { BlueprintOrError, Blueprints } from "../../../data/blueprint"; +import { BlueprintScriptConfig } from "../../../data/script"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; + +@customElement("blueprint-generic-editor") +export abstract class HaBlueprintGenericEditor extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public isWide = false; + + @property({ type: Boolean }) public disabled = false; + + @property({ type: Boolean, reflect: true }) public narrow = false; + + @state() protected _blueprints?: Blueprints; + + protected firstUpdated(changedProps) { + super.firstUpdated(changedProps); + this._getBlueprints(); + } + + protected get _blueprint(): BlueprintOrError | undefined { + if (!this._blueprints) { + return undefined; + } + return this._blueprints[this._config.use_blueprint.path]; + } + + protected abstract get _config(): + | BlueprintAutomationConfig + | BlueprintScriptConfig; + + protected renderCard() { + const blueprint = this._blueprint; + return html` + +
    + ${this._blueprints + ? Object.keys(this._blueprints).length + ? html` + + ` + : this.hass.localize( + "ui.panel.config.automation.editor.blueprint.no_blueprints" + ) + : html``} +
    + + ${this._config.use_blueprint.path + ? blueprint && "error" in blueprint + ? html`

    + There is an error in this Blueprint: ${blueprint.error} +

    ` + : html`${blueprint?.metadata.description + ? html`` + : ""} + ${blueprint?.metadata?.input && + Object.keys(blueprint.metadata.input).length + ? Object.entries(blueprint.metadata.input).map( + ([key, value]) => { + const selector = value?.selector ?? { text: undefined }; + const type = Object.keys(selector)[0]; + const enhancedSelector = [ + "action", + "condition", + "trigger", + ].includes(type) + ? { + [type]: { + ...selector[type], + path: [key], + }, + } + : selector; + + return html` + ${value?.name || key} + + ${html``} + `; + } + ) + : html`

    + ${this.hass.localize( + "ui.panel.config.automation.editor.blueprint.no_inputs" + )} +

    `}` + : ""} +
    + `; + } + + protected abstract _getBlueprints(); + + private _blueprintChanged(ev) { + ev.stopPropagation(); + if (this._config.use_blueprint.path === ev.detail.value) { + return; + } + fireEvent(this, "value-changed", { + value: { + ...this._config, + use_blueprint: { + path: ev.detail.value, + }, + }, + }); + } + + private _inputChanged(ev) { + ev.stopPropagation(); + const target = ev.target as any; + const key = target.key; + const value = ev.detail ? ev.detail.value : target.value; + if ( + (this._config.use_blueprint.input && + this._config.use_blueprint.input[key] === value) || + (!this._config.use_blueprint.input && value === "") + ) { + return; + } + const input = { ...this._config.use_blueprint.input, [key]: value }; + + fireEvent(this, "value-changed", { + value: { + ...this._config, + use_blueprint: { + ...this._config.use_blueprint, + input, + }, + }, + }); + } + + private _itemMoved(ev) { + ev.stopPropagation(); + const { oldIndex, newIndex, oldPath, newPath } = ev.detail; + + const input = nestedArrayMove( + this._config.use_blueprint.input, + oldIndex, + newIndex, + oldPath, + newPath + ); + + fireEvent(this, "value-changed", { + value: { + ...this._config, + use_blueprint: { + ...this._config.use_blueprint, + input, + }, + }, + }); + } + + protected _duplicate() { + fireEvent(this, "duplicate"); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + :host { + display: block; + } + ha-card.blueprint { + margin: 0 auto; + } + .padding { + padding: 16px; + } + .link-button-row { + padding: 14px; + } + .blueprint-picker-container { + padding: 0 16px 16px; + } + ha-textfield, + ha-blueprint-picker { + display: block; + } + h3 { + margin: 16px; + } + .introduction { + margin-top: 0; + margin-bottom: 12px; + } + .introduction a { + color: var(--primary-color); + } + p { + margin-bottom: 0; + } + .description { + margin-bottom: 16px; + } + ha-settings-row { + --paper-time-input-justify-content: flex-end; + --settings-row-content-width: 100%; + --settings-row-prefix-display: contents; + border-top: 1px solid var(--divider-color); + } + ha-alert { + margin-bottom: 16px; + display: block; + } + ha-alert.re-order { + border-radius: var(--ha-card-border-radius, 12px); + overflow: hidden; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "blueprint-generic-editor": HaBlueprintGenericEditor; + } +} diff --git a/src/panels/config/script/blueprint-script-editor.ts b/src/panels/config/script/blueprint-script-editor.ts index be455965f4..c233eae662 100644 --- a/src/panels/config/script/blueprint-script-editor.ts +++ b/src/panels/config/script/blueprint-script-editor.ts @@ -1,52 +1,19 @@ -import { css, CSSResultGroup, html, LitElement } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { fireEvent } from "../../../common/dom/fire_event"; -import { nestedArrayMove } from "../../../common/util/array-move"; +import { html } from "lit"; +import { customElement, property } from "lit/decorators"; import "../../../components/ha-alert"; -import "../../../components/ha-blueprint-picker"; -import "../../../components/ha-card"; -import "../../../components/ha-circular-progress"; -import "../../../components/ha-markdown"; -import "../../../components/ha-selector/ha-selector"; -import "../../../components/ha-settings-row"; -import { - BlueprintOrError, - Blueprints, - fetchBlueprints, -} from "../../../data/blueprint"; import { BlueprintScriptConfig } from "../../../data/script"; -import { haStyle } from "../../../resources/styles"; -import { HomeAssistant } from "../../../types"; -import "../ha-config-section"; +import { fetchBlueprints } from "../../../data/blueprint"; +import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor"; @customElement("blueprint-script-editor") -export class HaBlueprintScriptEditor extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; - - @property({ type: Boolean }) public isWide = false; - - @property({ reflect: true, type: Boolean }) public narrow = false; - - @property({ type: Boolean }) public disabled = false; - +export class HaBlueprintScriptEditor extends HaBlueprintGenericEditor { @property({ attribute: false }) public config!: BlueprintScriptConfig; - @state() private _blueprints?: Blueprints; - - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - this._getBlueprints(); - } - - private get _blueprint(): BlueprintOrError | undefined { - if (!this._blueprints) { - return undefined; - } - return this._blueprints[this.config.use_blueprint.path]; + protected get _config(): BlueprintScriptConfig { + return this.config; } protected render() { - const blueprint = this._blueprint; return html` ${this.disabled ? html` @@ -56,228 +23,14 @@ export class HaBlueprintScriptEditor extends LitElement {
    ` : ""} - -
    - ${this._blueprints - ? Object.keys(this._blueprints).length - ? html` - - ` - : this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_blueprints" - ) - : html``} -
    - ${this.config.use_blueprint.path - ? blueprint && "error" in blueprint - ? html`

    - There is an error in this Blueprint: ${blueprint.error} -

    ` - : html`${blueprint?.metadata.description - ? html`` - : ""} - ${blueprint?.metadata?.input && - Object.keys(blueprint.metadata.input).length - ? Object.entries(blueprint.metadata.input).map( - ([key, value]) => { - const selector = value?.selector ?? { text: undefined }; - const type = Object.keys(selector)[0]; - const enhancedSelector = [ - "action", - "condition", - "trigger", - ].includes(type) - ? { - [type]: { - ...selector[type], - path: [key], - }, - } - : selector; - - return html` - ${value?.name || key} - - ${html``} - `; - } - ) - : html`

    - ${this.hass.localize( - "ui.panel.config.automation.editor.blueprint.no_inputs" - )} -

    `}` - : ""} -
    + ${this.renderCard()} `; } - private async _getBlueprints() { + protected async _getBlueprints() { this._blueprints = await fetchBlueprints(this.hass, "script"); } - - private _blueprintChanged(ev) { - ev.stopPropagation(); - if (this.config.use_blueprint.path === ev.detail.value) { - return; - } - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - path: ev.detail.value, - }, - }, - }); - } - - private _inputChanged(ev) { - ev.stopPropagation(); - const target = ev.target as any; - const key = target.key; - const value = ev.detail ? ev.detail.value : target.value; - if ( - (this.config.use_blueprint.input && - this.config.use_blueprint.input[key] === value) || - (!this.config.use_blueprint.input && value === "") - ) { - return; - } - const input = { ...this.config.use_blueprint.input, [key]: value }; - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - - private _itemMoved(ev) { - ev.stopPropagation(); - const { oldIndex, newIndex, oldPath, newPath } = ev.detail; - - const input = nestedArrayMove( - this.config.use_blueprint.input, - oldIndex, - newIndex, - oldPath, - newPath - ); - - fireEvent(this, "value-changed", { - value: { - ...this.config, - use_blueprint: { - ...this.config.use_blueprint, - input, - }, - }, - }); - } - - private _duplicate() { - fireEvent(this, "duplicate"); - } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - :host { - display: block; - } - ha-card.blueprint { - margin: 0 auto; - } - .padding { - padding: 16px; - } - .link-button-row { - padding: 14px; - } - .blueprint-picker-container { - padding: 0 16px 16px; - } - ha-textfield, - ha-blueprint-picker { - display: block; - } - h3 { - margin: 16px; - } - .introduction { - margin-top: 0; - margin-bottom: 12px; - } - .introduction a { - color: var(--primary-color); - } - p { - margin-bottom: 0; - } - .description { - margin-bottom: 16px; - } - ha-settings-row { - --paper-time-input-justify-content: flex-end; - --settings-row-content-width: 100%; - --settings-row-prefix-display: contents; - border-top: 1px solid var(--divider-color); - } - ha-alert { - margin-bottom: 16px; - display: block; - } - ha-alert.re-order { - border-radius: var(--ha-card-border-radius, 12px); - overflow: hidden; - } - `, - ]; - } } - declare global { interface HTMLElementTagNameMap { "blueprint-script-editor": HaBlueprintScriptEditor; From 155098bc410c2edb6d5ae81247fb7794d1f312c0 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 Feb 2024 13:30:10 +0100 Subject: [PATCH 106/116] Center section in section view (#19904) --- .../lovelace/views/hui-sections-view.ts | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts index fcd430c8ea..cbf5af8570 100644 --- a/src/panels/lovelace/views/hui-sections-view.ts +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -2,6 +2,7 @@ import { mdiArrowAll, mdiDelete, mdiPencil, mdiViewGridPlus } from "@mdi/js"; import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; +import { styleMap } from "lit/directives/style-map"; import "../../../components/ha-icon-button"; import "../../../components/ha-sortable"; import "../../../components/ha-svg-icon"; @@ -64,7 +65,14 @@ export class SectionsView extends LitElement implements LovelaceViewElement { draggable-selector=".section" .rollback=${false} > -
    +
    ${repeat( sectionsConfig, (sectionConfig) => this._getKey(sectionConfig), @@ -230,25 +238,33 @@ export class SectionsView extends LitElement implements LovelaceViewElement { } .container { - --column-count: 3; + --grid-gap: 20px; + --grid-max-width: 1400px; + --grid-cell-max-width: 500px; + --grid-cell-min-width: 320px; display: grid; - grid-template-columns: repeat(var(--column-count), minmax(0, 1fr)); - gap: 8px 20px; - max-width: 1400px; - padding: 20px; + grid-template-columns: repeat( + auto-fit, + minmax(var(--grid-cell-min-width), 1fr) + ); + justify-content: center; + gap: 8px var(--grid-gap); + padding: var(--grid-gap); + box-sizing: border-box; + max-width: min( + calc( + var(--cell-count) * (var(--grid-cell-max-width) + var(--grid-gap)) + + var(--grid-gap) + ), + var(--grid-max-width) + ); margin: 0 auto; } - @media (max-width: 1200px) { - .container { - --column-count: 2; - } - } - @media (max-width: 600px) { .container { - --column-count: 1; - padding: 8px; + grid-template-columns: 1fr; + --grid-gap: 8px; } } From ebdbab81d3bbd032b5b9f72d1954d273131e6888 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:51:59 +0100 Subject: [PATCH 107/116] Mobile view calendar improvements (#19218) * Mobile view calendar improvements * Remove very narrow * Revert default choice --- src/panels/calendar/ha-full-calendar.ts | 12 +++++++++++- src/panels/lovelace/cards/hui-calendar-card.ts | 11 +++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/panels/calendar/ha-full-calendar.ts b/src/panels/calendar/ha-full-calendar.ts index e1b3b08296..46ceac93c9 100644 --- a/src/panels/calendar/ha-full-calendar.ts +++ b/src/panels/calendar/ha-full-calendar.ts @@ -188,7 +188,7 @@ export class HAFullCalendar extends LitElement {
    -
    +
    * { + margin-bottom: 5px; + box-sizing: border-box; + } + .today { margin-right: 20px; margin-inline-end: 20px; diff --git a/src/panels/lovelace/cards/hui-calendar-card.ts b/src/panels/lovelace/cards/hui-calendar-card.ts index 047118828a..1a31a6d1cf 100644 --- a/src/panels/lovelace/cards/hui-calendar-card.ts +++ b/src/panels/lovelace/cards/hui-calendar-card.ts @@ -69,8 +69,6 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard { @state() private _narrow = false; - @state() private _veryNarrow = false; - @state() private _error?: string = undefined; private _startDate?: Date; @@ -121,9 +119,11 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard { return nothing; } - const views: FullCalendarView[] = this._veryNarrow - ? ["listWeek"] - : ["dayGridMonth", "dayGridDay", "listWeek"]; + const views: FullCalendarView[] = [ + "dayGridMonth", + "dayGridDay", + "listWeek", + ]; return html` @@ -206,7 +206,6 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard { return; } this._narrow = card.offsetWidth < 870; - this._veryNarrow = card.offsetWidth < 350; } private async _attachObserver(): Promise { From 4dc154201af972bae20d0b523909701e9a6b4886 Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 28 Feb 2024 07:55:11 -0500 Subject: [PATCH 108/116] Support color names in energy themes (#19597) * Support color names in energy themes * fix yarn --- package.json | 2 + .../lovelace/cards/energy/common/color.ts | 64 +++++ .../cards/energy/hui-energy-gas-graph-card.ts | 47 ++-- .../energy/hui-energy-solar-graph-card.ts | 47 ++-- .../energy/hui-energy-sources-table-card.ts | 244 +++++++++--------- .../energy/hui-energy-usage-graph-card.ts | 119 +++------ .../energy/hui-energy-water-graph-card.ts | 47 ++-- yarn.lock | 9 + 8 files changed, 284 insertions(+), 295 deletions(-) create mode 100644 src/panels/lovelace/cards/energy/common/color.ts diff --git a/package.json b/package.json index 60688e33b7..25616cee3e 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "@webcomponents/webcomponentsjs": "2.8.0", "app-datepicker": "5.1.1", "chart.js": "4.4.1", + "color-name": "~1.1.4", "comlink": "4.4.1", "core-js": "3.36.0", "cropperjs": "1.6.1", @@ -170,6 +171,7 @@ "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.13", "@types/chromecast-caf-sender": "1.0.8", + "@types/color-name": "^1", "@types/glob": "8.1.0", "@types/html-minifier-terser": "7.0.2", "@types/js-yaml": "4.0.9", diff --git a/src/panels/lovelace/cards/energy/common/color.ts b/src/panels/lovelace/cards/energy/common/color.ts new file mode 100644 index 0000000000..f9f02f3674 --- /dev/null +++ b/src/panels/lovelace/cards/energy/common/color.ts @@ -0,0 +1,64 @@ +import colors from "color-name"; +import { + hex2rgb, + lab2rgb, + rgb2hex, + rgb2lab, +} from "../../../../../common/color/convert-color"; +import { labBrighten, labDarken } from "../../../../../common/color/lab"; + +export function getEnergyColor( + computedStyles: CSSStyleDeclaration, + darkMode: boolean, + background: boolean, + compare: boolean, + propertyName: string, + idx?: number +): string { + const themeIdxColor = computedStyles + .getPropertyValue(propertyName + "-" + idx) + .trim(); + + const themeColor = + themeIdxColor.length > 0 + ? themeIdxColor + : computedStyles.getPropertyValue(propertyName).trim(); + + let hexColor; + if (themeColor.startsWith("#")) { + hexColor = themeColor; + } else { + const rgbFromColorName = colors[themeColor]; + if (!rgbFromColorName) { + // We have a named color, and there's nothing in the table, + // so nothing further we can do with it. + // Compare/border/background color will all be the same. + return themeColor; + } + hexColor = rgb2hex(rgbFromColorName); + } + + if (themeIdxColor.length === 0 && idx) { + // Brighten or darken the color based on set position. + // Skip if theme already provides a color for this set. + + hexColor = rgb2hex( + lab2rgb( + darkMode + ? labBrighten(rgb2lab(hex2rgb(hexColor)), idx) + : labDarken(rgb2lab(hex2rgb(hexColor)), idx) + ) + ); + } + + if (compare) { + if (background) { + hexColor += "32"; + } else { + hexColor += "7F"; + } + } else if (background) { + hexColor += "7F"; + } + return hexColor; +} 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 e55383015b..baf3b2a7a7 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 @@ -17,13 +17,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -204,16 +198,12 @@ export class HuiEnergyGasGraphCard const datasets: ChartDataset<"bar", ScatterDataPoint[]>[] = []; const computedStyles = getComputedStyle(this); - const gasColor = computedStyles - .getPropertyValue("--energy-gas-color") - .trim(); datasets.push( ...this._processDataSet( energyData.stats, energyData.statsMetadata, gasSources, - gasColor, computedStyles ) ); @@ -235,7 +225,6 @@ export class HuiEnergyGasGraphCard energyData.statsCompare, energyData.statsMetadata, gasSources, - gasColor, computedStyles, true ) @@ -257,28 +246,12 @@ export class HuiEnergyGasGraphCard statistics: Statistics, statisticsMetaData: Record, gasSources: GasSourceTypeEnergyPreference[], - gasColor: string, computedStyles: CSSStyleDeclaration, compare = false ) { const data: ChartDataset<"bar", ScatterDataPoint[]>[] = []; gasSources.forEach((source, idx) => { - let borderColor = computedStyles - .getPropertyValue("--energy-gas-color-" + idx) - .trim(); - if (borderColor.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(gasColor)), idx) - : labDarken(rgb2lab(hex2rgb(gasColor)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : gasColor; - } - let prevStart: number | null = null; const gasConsumptionData: ScatterDataPoint[] = []; @@ -317,8 +290,22 @@ export class HuiEnergyGasGraphCard source.stat_energy_from, statisticsMetaData[source.stat_energy_from] ), - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + "--energy-gas-color", + idx + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + "--energy-gas-color", + idx + ), data: gasConsumptionData, order: 1, stack: "gas", 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 d5f3e767d0..40d91584dc 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 @@ -22,13 +22,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -226,16 +220,12 @@ export class HuiEnergySolarGraphCard const datasets: ChartDataset<"bar" | "line">[] = []; const computedStyles = getComputedStyle(this); - const solarColor = computedStyles - .getPropertyValue("--energy-solar-color") - .trim(); datasets.push( ...this._processDataSet( energyData.stats, energyData.statsMetadata, solarSources, - solarColor, computedStyles ) ); @@ -257,7 +247,6 @@ export class HuiEnergySolarGraphCard energyData.statsCompare, energyData.statsMetadata, solarSources, - solarColor, computedStyles, true ) @@ -292,28 +281,12 @@ export class HuiEnergySolarGraphCard statistics: Statistics, statisticsMetaData: Record, solarSources: SolarSourceTypeEnergyPreference[], - solarColor: string, computedStyles: CSSStyleDeclaration, compare = false ) { const data: ChartDataset<"bar", ScatterDataPoint[]>[] = []; solarSources.forEach((source, idx) => { - let borderColor = computedStyles - .getPropertyValue("--energy-solar-color-" + idx) - .trim(); - if (borderColor.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(solarColor)), idx) - : labDarken(rgb2lab(hex2rgb(solarColor)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : solarColor; - } - let prevStart: number | null = null; const solarProductionData: ScatterDataPoint[] = []; @@ -357,8 +330,22 @@ export class HuiEnergySolarGraphCard ), } ), - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + "--energy-solar-color", + idx + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + "--energy-solar-color", + idx + ), data: solarProductionData, order: 1, stack: "solar", diff --git a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts index 2be70f203b..4373c30bd1 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-sources-table-card.ts @@ -12,14 +12,8 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; import { formatNumber } from "../../../../common/number/format_number"; +import { getEnergyColor } from "./common/color"; import "../../../../components/ha-card"; import { EnergyData, @@ -38,6 +32,16 @@ import { LovelaceCard } from "../../types"; import { EnergySourcesTableCardConfig } from "../types"; import { hasConfigChanged } from "../../common/has-changed"; +const colorPropertyMap = { + grid_return: "--energy-grid-return-color", + grid_consumption: "--energy-grid-consumption-color", + battery_in: "--energy-battery-in-color", + battery_out: "--energy-battery-out-color", + solar: "--energy-solar-color", + gas: "--energy-gas-color", + water: "--energy-water-color", +}; + @customElement("hui-energy-sources-table-card") export class HuiEnergySourcesTableCard extends SubscribeMixin(LitElement) @@ -77,27 +81,6 @@ export class HuiEnergySourcesTableCard ); } - private _getColor( - computedStyles: CSSStyleDeclaration, - propertyName: string, - baseColor: string, - idx: number - ): string { - let color = computedStyles - .getPropertyValue(propertyName + "-" + idx) - .trim(); - if (color.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(baseColor)), idx) - : labDarken(rgb2lab(hex2rgb(baseColor)), idx) - : undefined; - color = modifiedColor ? rgb2hex(lab2rgb(modifiedColor)) : baseColor; - } - return color; - } - protected render() { if (!this.hass || !this._config) { return nothing; @@ -133,38 +116,7 @@ export class HuiEnergySourcesTableCard const types = energySourcesByType(this._data.prefs); - const colorPropertyMap = { - grid_return: "--energy-grid-return-color", - grid_consumption: "--energy-grid-consumption-color", - battery_in: "--energy-battery-in-color", - battery_out: "--energy-battery-out-color", - solar: "--energy-solar-color", - gas: "--energy-gas-color", - water: "--energy-water-color", - }; - const computedStyles = getComputedStyle(this); - const solarColor = computedStyles - .getPropertyValue(colorPropertyMap.solar) - .trim(); - const batteryFromColor = computedStyles - .getPropertyValue(colorPropertyMap.battery_out) - .trim(); - const batteryToColor = computedStyles - .getPropertyValue(colorPropertyMap.battery_in) - .trim(); - const returnColor = computedStyles - .getPropertyValue(colorPropertyMap.grid_return) - .trim(); - const consumptionColor = computedStyles - .getPropertyValue(colorPropertyMap.grid_consumption) - .trim(); - const gasColor = computedStyles - .getPropertyValue(colorPropertyMap.gas) - .trim(); - const waterColor = computedStyles - .getPropertyValue(colorPropertyMap.water) - .trim(); const showCosts = types.grid?.[0].flow_from.some( @@ -273,20 +225,27 @@ export class HuiEnergySourcesTableCard 0; totalSolarCompare += compareEnergy; - const color = this._getColor( - computedStyles, - colorPropertyMap.solar, - solarColor, - idx - ); - return html`
    @@ -371,26 +330,27 @@ export class HuiEnergySourcesTableCard 0; totalBatteryCompare += energyFromCompare - energyToCompare; - const fromColor = this._getColor( - computedStyles, - colorPropertyMap.battery_out, - batteryFromColor, - idx - ); - const toColor = this._getColor( - computedStyles, - colorPropertyMap.battery_in, - batteryToColor, - idx - ); - return html`
    @@ -426,8 +386,22 @@ export class HuiEnergySourcesTableCard
    @@ -534,20 +508,27 @@ export class HuiEnergySourcesTableCard totalGridCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.grid_consumption, - consumptionColor, - idx - ); - return html`
    @@ -638,20 +619,27 @@ export class HuiEnergySourcesTableCard totalGridCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.grid_return, - returnColor, - idx - ); - return html`
    @@ -794,20 +782,27 @@ export class HuiEnergySourcesTableCard totalGasCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.gas, - gasColor, - idx - ); - return html`
    @@ -945,20 +940,27 @@ export class HuiEnergySourcesTableCard totalWaterCostCompare += costCompare; } - const color = this._getColor( - computedStyles, - colorPropertyMap.water, - waterColor, - idx - ); - return html`
    diff --git a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts index 5b28a4195b..7b7c45db80 100644 --- a/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts +++ b/src/panels/lovelace/cards/energy/hui-energy-usage-graph-card.ts @@ -17,13 +17,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -41,10 +35,14 @@ import { EnergyUsageGraphCardConfig } from "../types"; import { hasConfigChanged } from "../../common/has-changed"; import { getCommonOptions } from "./common/energy-chart-options"; -interface ColorSet { - base: string; - overrides?: Record; -} +const colorPropertyMap = { + to_grid: "--energy-grid-return-color", + to_battery: "--energy-battery-in-color", + from_grid: "--energy-grid-consumption-color", + used_grid: "--energy-grid-consumption-color", + used_solar: "--energy-solar-color", + used_battery: "--energy-battery-out-color", +}; @customElement("hui-energy-usage-graph-card") export class HuiEnergyUsageGraphCard @@ -263,47 +261,9 @@ export class HuiEnergyUsageGraphCard const computedStyles = getComputedStyle(this); - const colorPropertyMap = { - to_grid: "--energy-grid-return-color", - to_battery: "--energy-battery-in-color", - from_grid: "--energy-grid-consumption-color", - used_grid: "--energy-grid-consumption-color", - used_solar: "--energy-solar-color", - used_battery: "--energy-battery-out-color", - }; - - const colors = { - to_grid: { - base: computedStyles.getPropertyValue(colorPropertyMap.to_grid).trim(), - }, - to_battery: { - base: computedStyles - .getPropertyValue(colorPropertyMap.to_battery) - .trim(), - }, - from_grid: { - base: computedStyles - .getPropertyValue(colorPropertyMap.from_grid) - .trim(), - }, - used_grid: { - base: computedStyles - .getPropertyValue(colorPropertyMap.used_grid) - .trim(), - }, - used_solar: { - base: computedStyles - .getPropertyValue(colorPropertyMap.used_solar) - .trim(), - }, - used_battery: { - base: computedStyles - .getPropertyValue(colorPropertyMap.used_battery) - .trim(), - }, - }; - - Object.entries(colorPropertyMap).forEach(([key, colorProp]) => { + const colorIndices: Record> = {}; + Object.keys(colorPropertyMap).forEach((key) => { + colorIndices[key] = {}; if ( key === "used_grid" || key === "used_solar" || @@ -311,15 +271,9 @@ export class HuiEnergyUsageGraphCard ) { return; } - colors[key].overrides = []; if (statIds[key]) { Object.values(statIds[key]).forEach((id, idx) => { - const override = computedStyles - .getPropertyValue(colorProp + "-" + idx) - .trim(); - if (override.length > 0) { - colors[key].overrides[id] = override; - } + colorIndices[key][id as string] = idx; }); } }); @@ -347,7 +301,8 @@ export class HuiEnergyUsageGraphCard energyData.stats, energyData.statsMetadata, statIds, - colors, + colorIndices, + computedStyles, labels, false ) @@ -370,7 +325,8 @@ export class HuiEnergyUsageGraphCard energyData.statsCompare, energyData.statsMetadata, statIds, - colors, + colorIndices, + computedStyles, labels, true ) @@ -392,14 +348,8 @@ export class HuiEnergyUsageGraphCard to_battery?: string[] | undefined; from_battery?: string[] | undefined; }, - colors: { - to_grid: ColorSet; - to_battery: ColorSet; - from_grid: ColorSet; - used_grid: ColorSet; - used_solar: ColorSet; - used_battery: ColorSet; - }, + colorIndices: Record>, + computedStyles: CSSStyleDeclaration, labels: { used_grid: string; used_solar: string; @@ -553,19 +503,6 @@ export class HuiEnergyUsageGraphCard Object.entries(combinedData).forEach(([type, sources]) => { Object.entries(sources).forEach(([statId, source], idx) => { - let borderColor = colors[type].overrides?.[statId]; - if (!borderColor) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(colors[type].base)), idx) - : labDarken(rgb2lab(hex2rgb(colors[type].base)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : colors[type].base; - } - const points: ScatterDataPoint[] = []; // Process chart data. for (const key of uniqueKeys) { @@ -600,8 +537,22 @@ export class HuiEnergyUsageGraphCard : type === "to_battery" ? Object.keys(combinedData).length : idx + 2, - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + colorPropertyMap[type], + colorIndices[type]?.[statId] + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + colorPropertyMap[type], + colorIndices[type]?.[statId] + ), stack: "stack", data: points, xAxisID: compare ? "xAxisCompare" : undefined, 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 e05d6fb665..d5011fc9cb 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 @@ -17,13 +17,7 @@ import { import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import memoizeOne from "memoize-one"; -import { - hex2rgb, - lab2rgb, - rgb2hex, - rgb2lab, -} from "../../../../common/color/convert-color"; -import { labBrighten, labDarken } from "../../../../common/color/lab"; +import { getEnergyColor } from "./common/color"; import { formatNumber } from "../../../../common/number/format_number"; import "../../../../components/chart/ha-chart-base"; import "../../../../components/ha-card"; @@ -202,16 +196,12 @@ export class HuiEnergyWaterGraphCard const datasets: ChartDataset<"bar", ScatterDataPoint[]>[] = []; const computedStyles = getComputedStyle(this); - const waterColor = computedStyles - .getPropertyValue("--energy-water-color") - .trim(); datasets.push( ...this._processDataSet( energyData.stats, energyData.statsMetadata, waterSources, - waterColor, computedStyles ) ); @@ -233,7 +223,6 @@ export class HuiEnergyWaterGraphCard energyData.statsCompare, energyData.statsMetadata, waterSources, - waterColor, computedStyles, true ) @@ -255,28 +244,12 @@ export class HuiEnergyWaterGraphCard statistics: Statistics, statisticsMetaData: Record, waterSources: WaterSourceTypeEnergyPreference[], - waterColor: string, computedStyles: CSSStyleDeclaration, compare = false ) { const data: ChartDataset<"bar", ScatterDataPoint[]>[] = []; waterSources.forEach((source, idx) => { - let borderColor = computedStyles - .getPropertyValue("--energy-water-color-" + idx) - .trim(); - if (borderColor.length === 0) { - const modifiedColor = - idx > 0 - ? this.hass.themes.darkMode - ? labBrighten(rgb2lab(hex2rgb(waterColor)), idx) - : labDarken(rgb2lab(hex2rgb(waterColor)), idx) - : undefined; - borderColor = modifiedColor - ? rgb2hex(lab2rgb(modifiedColor)) - : waterColor; - } - let prevStart: number | null = null; const waterConsumptionData: ScatterDataPoint[] = []; @@ -315,8 +288,22 @@ export class HuiEnergyWaterGraphCard source.stat_energy_from, statisticsMetaData[source.stat_energy_from] ), - borderColor: compare ? borderColor + "7F" : borderColor, - backgroundColor: compare ? borderColor + "32" : borderColor + "7F", + borderColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + false, + compare, + "--energy-water-color", + idx + ), + backgroundColor: getEnergyColor( + computedStyles, + this.hass.themes.darkMode, + true, + compare, + "--energy-water-color", + idx + ), data: waterConsumptionData, order: 1, stack: "water", diff --git a/yarn.lock b/yarn.lock index 1a0ff9d448..88c2e9af1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4003,6 +4003,13 @@ __metadata: languageName: node linkType: hard +"@types/color-name@npm:^1": + version: 1.1.3 + resolution: "@types/color-name@npm:1.1.3" + checksum: 10/9060d16d0bce2cdf562d6da54e18c5f23e80308ccb58b725b9173a028818f27d8e01c8a5cd96952e76f11145a7388ed7d2f450fb4652f4760383834f2e698263 + languageName: node + linkType: hard + "@types/command-line-args@npm:^5.0.0": version: 5.2.3 resolution: "@types/command-line-args@npm:5.2.3" @@ -9605,6 +9612,7 @@ __metadata: "@types/babel__plugin-transform-runtime": "npm:7.9.5" "@types/chromecast-caf-receiver": "npm:6.0.13" "@types/chromecast-caf-sender": "npm:1.0.8" + "@types/color-name": "npm:^1" "@types/glob": "npm:8.1.0" "@types/html-minifier-terser": "npm:7.0.2" "@types/js-yaml": "npm:4.0.9" @@ -9635,6 +9643,7 @@ __metadata: babel-plugin-template-html-minifier: "npm:4.1.0" chai: "npm:5.1.0" chart.js: "npm:4.4.1" + color-name: "npm:~1.1.4" comlink: "npm:4.4.1" core-js: "npm:3.36.0" cropperjs: "npm:1.6.1" From 46394d0bf9b9efd9adcedbcd14dbe710fbd15a80 Mon Sep 17 00:00:00 2001 From: Charles Baynham Date: Wed, 28 Feb 2024 12:56:30 +0000 Subject: [PATCH 109/116] Always show "create new zone from current location" option (#19649) --- src/dialogs/more-info/controls/more-info-person.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dialogs/more-info/controls/more-info-person.ts b/src/dialogs/more-info/controls/more-info-person.ts index bba6427c8f..be00a04095 100644 --- a/src/dialogs/more-info/controls/more-info-person.ts +++ b/src/dialogs/more-info/controls/more-info-person.ts @@ -34,7 +34,6 @@ class MoreInfoPerson extends LitElement { : ""} ${!__DEMO__ && this.hass.user?.is_admin && - this.stateObj.state === "not_home" && this.stateObj.attributes.latitude && this.stateObj.attributes.longitude ? html` From c47c6e358bba7eaeafc1d68352c6a9da37ca4e71 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:05:53 +0000 Subject: [PATCH 110/116] Update dependency color-name to v2 (#19906) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 25616cee3e..d3957c62c6 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "@webcomponents/webcomponentsjs": "2.8.0", "app-datepicker": "5.1.1", "chart.js": "4.4.1", - "color-name": "~1.1.4", + "color-name": "~2.0.0", "comlink": "4.4.1", "core-js": "3.36.0", "cropperjs": "1.6.1", diff --git a/yarn.lock b/yarn.lock index 88c2e9af1d..975eb3a21a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6758,6 +6758,13 @@ __metadata: languageName: node linkType: hard +"color-name@npm:~2.0.0": + version: 2.0.0 + resolution: "color-name@npm:2.0.0" + checksum: 10/10a1addae41de2987d6b90dbd3cfade266c2e6f680ce21749911df4493b4fae07654862c6b5358bdd13e155461acb4eedaa5e0ba172bf13542cdcca10866cf2b + languageName: node + linkType: hard + "color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" @@ -9643,7 +9650,7 @@ __metadata: babel-plugin-template-html-minifier: "npm:4.1.0" chai: "npm:5.1.0" chart.js: "npm:4.4.1" - color-name: "npm:~1.1.4" + color-name: "npm:~2.0.0" comlink: "npm:4.4.1" core-js: "npm:3.36.0" cropperjs: "npm:1.6.1" From b982884933a848c988592b4689e6ff8b0353eda4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 08:26:26 -0500 Subject: [PATCH 111/116] Pin dependencies (#19905) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 ++-- yarn.lock | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index d3957c62c6..4109cdfb9d 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "@webcomponents/webcomponentsjs": "2.8.0", "app-datepicker": "5.1.1", "chart.js": "4.4.1", - "color-name": "~2.0.0", + "color-name": "2.0.0", "comlink": "4.4.1", "core-js": "3.36.0", "cropperjs": "1.6.1", @@ -171,7 +171,7 @@ "@types/babel__plugin-transform-runtime": "7.9.5", "@types/chromecast-caf-receiver": "6.0.13", "@types/chromecast-caf-sender": "1.0.8", - "@types/color-name": "^1", + "@types/color-name": "1.1.3", "@types/glob": "8.1.0", "@types/html-minifier-terser": "7.0.2", "@types/js-yaml": "4.0.9", diff --git a/yarn.lock b/yarn.lock index 975eb3a21a..874b6ffc71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4003,7 +4003,7 @@ __metadata: languageName: node linkType: hard -"@types/color-name@npm:^1": +"@types/color-name@npm:1.1.3": version: 1.1.3 resolution: "@types/color-name@npm:1.1.3" checksum: 10/9060d16d0bce2cdf562d6da54e18c5f23e80308ccb58b725b9173a028818f27d8e01c8a5cd96952e76f11145a7388ed7d2f450fb4652f4760383834f2e698263 @@ -6751,6 +6751,13 @@ __metadata: languageName: node linkType: hard +"color-name@npm:2.0.0": + version: 2.0.0 + resolution: "color-name@npm:2.0.0" + checksum: 10/10a1addae41de2987d6b90dbd3cfade266c2e6f680ce21749911df4493b4fae07654862c6b5358bdd13e155461acb4eedaa5e0ba172bf13542cdcca10866cf2b + languageName: node + linkType: hard + "color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" @@ -6758,13 +6765,6 @@ __metadata: languageName: node linkType: hard -"color-name@npm:~2.0.0": - version: 2.0.0 - resolution: "color-name@npm:2.0.0" - checksum: 10/10a1addae41de2987d6b90dbd3cfade266c2e6f680ce21749911df4493b4fae07654862c6b5358bdd13e155461acb4eedaa5e0ba172bf13542cdcca10866cf2b - languageName: node - linkType: hard - "color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" @@ -9619,7 +9619,7 @@ __metadata: "@types/babel__plugin-transform-runtime": "npm:7.9.5" "@types/chromecast-caf-receiver": "npm:6.0.13" "@types/chromecast-caf-sender": "npm:1.0.8" - "@types/color-name": "npm:^1" + "@types/color-name": "npm:1.1.3" "@types/glob": "npm:8.1.0" "@types/html-minifier-terser": "npm:7.0.2" "@types/js-yaml": "npm:4.0.9" @@ -9650,7 +9650,7 @@ __metadata: babel-plugin-template-html-minifier: "npm:4.1.0" chai: "npm:5.1.0" chart.js: "npm:4.4.1" - color-name: "npm:~2.0.0" + color-name: "npm:2.0.0" comlink: "npm:4.4.1" core-js: "npm:3.36.0" cropperjs: "npm:1.6.1" From 94f74308d8242067a394b095b8168194b5bdd847 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 28 Feb 2024 14:29:00 +0100 Subject: [PATCH 112/116] Escape special characters of automation ID in URL (#19790) * Escape special character of automation ID in URL * Use encodeURIComponent --- src/panels/config/areas/ha-config-area-page.ts | 2 +- src/panels/config/automation/ha-automation-editor.ts | 10 ++++++++-- src/panels/config/automation/ha-automation-picker.ts | 10 +++++++--- src/panels/config/automation/ha-automation-trace.ts | 8 ++++++-- src/panels/config/devices/ha-config-device-page.ts | 2 +- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/panels/config/areas/ha-config-area-page.ts b/src/panels/config/areas/ha-config-area-page.ts index 18ce4dd5b9..64b05b126b 100644 --- a/src/panels/config/areas/ha-config-area-page.ts +++ b/src/panels/config/areas/ha-config-area-page.ts @@ -568,7 +568,7 @@ class HaConfigAreaPage extends LitElement { diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts index 04343c20ec..11571368c4 100644 --- a/src/panels/config/automation/ha-automation-editor.ts +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -172,7 +172,11 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { ${stateObj && this._config && this.narrow - ? html` + ? html` ${this.hass.localize( "ui.panel.config.automation.editor.show_trace" @@ -563,7 +567,9 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) { if (this._config?.id) { const result = await this.confirmUnsavedChanged(); if (result) { - navigate(`/config/automation/trace/${this._config.id}`); + navigate( + `/config/automation/trace/${encodeURIComponent(this._config.id)}` + ); } } } diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index 3bb5802c7d..db23980b2d 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -435,7 +435,9 @@ class HaAutomationPicker extends LitElement { }); return; } - navigate(`/config/automation/trace/${automation.attributes.id}`); + navigate( + `/config/automation/trace/${encodeURIComponent(automation.attributes.id)}` + ); } private async _toggle(automation): Promise { @@ -530,9 +532,11 @@ class HaAutomationPicker extends LitElement { ); if (automation?.attributes.id) { - navigate(`/config/automation/edit/${automation.attributes.id}`); + navigate( + `/config/automation/edit/${encodeURIComponent(automation.attributes.id)}` + ); } else { - navigate(`/config/automation/show/${ev.detail.id}`); + navigate(`/config/automation/show/${encodeURIComponent(ev.detail.id)}`); } } diff --git a/src/panels/config/automation/ha-automation-trace.ts b/src/panels/config/automation/ha-automation-trace.ts index c752e562ed..cd52275ed7 100644 --- a/src/panels/config/automation/ha-automation-trace.ts +++ b/src/panels/config/automation/ha-automation-trace.ts @@ -106,7 +106,9 @@ export class HaAutomationTrace extends LitElement { ? html` @@ -140,7 +142,9 @@ export class HaAutomationTrace extends LitElement { ? html` ${this.hass.localize( diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 5179d3b2ca..4ea0dd37d2 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -431,7 +431,7 @@ export class HaConfigDevicePage extends LitElement { From dd98ec771db384ffd0c2774607d91de9826e92ba Mon Sep 17 00:00:00 2001 From: karwosts <32912880+karwosts@users.noreply.github.com> Date: Wed, 28 Feb 2024 10:20:56 -0500 Subject: [PATCH 113/116] Infer a limited history chart from state object (#19176) * Infer a limited history chart from state object * bugfix * bugfix * minor fixes from feedback --- .../chart/state-history-chart-line.ts | 2 +- .../chart/state-history-chart-timeline.ts | 2 +- src/components/chart/state-history-charts.ts | 34 ++++++++++++++----- .../timeline-chart/timeline-controller.ts | 4 ++- .../chart/timeline-chart/timeline-scale.ts | 4 +-- src/data/history.ts | 28 ++++++++++++--- src/dialogs/more-info/ha-more-info-history.ts | 1 + src/panels/history/ha-panel-history.ts | 2 ++ .../lovelace/cards/hui-history-graph-card.ts | 1 + 9 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index 5f6286ecef..0923fb0702 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -111,7 +111,7 @@ export class StateHistoryChartLine extends LitElement { config: this.hass.config, }, }, - suggestedMin: this.startTime, + min: this.startTime, suggestedMax: this.endTime, ticks: { maxRotation: 0, diff --git a/src/components/chart/state-history-chart-timeline.ts b/src/components/chart/state-history-chart-timeline.ts index 8bfb0fef8b..c70711ae64 100644 --- a/src/components/chart/state-history-chart-timeline.ts +++ b/src/components/chart/state-history-chart-timeline.ts @@ -114,7 +114,7 @@ export class StateHistoryChartTimeline extends LitElement { config: this.hass.config, }, }, - suggestedMin: this.startTime, + min: this.startTime, suggestedMax: this.endTime, ticks: { autoSkip: true, diff --git a/src/components/chart/state-history-charts.ts b/src/components/chart/state-history-charts.ts index 7f72aa5f2b..eb47c5b31a 100644 --- a/src/components/chart/state-history-charts.ts +++ b/src/components/chart/state-history-charts.ts @@ -233,16 +233,32 @@ export class StateHistoryCharts extends LitElement { new Date().getTime() - 60 * 60 * this.hoursToShow * 1000 ); } else { - this._computedStartTime = new Date( - (this.historyData?.timeline ?? []).reduce( - (minTime, stateInfo) => - Math.min( - minTime, - new Date(stateInfo.data[0].last_changed).getTime() - ), - new Date().getTime() - ) + let minTimeAll = (this.historyData?.timeline ?? []).reduce( + (minTime, stateInfo) => + Math.min( + minTime, + new Date(stateInfo.data[0].last_changed).getTime() + ), + new Date().getTime() ); + + minTimeAll = (this.historyData?.line ?? []).reduce( + (minTimeLine, line) => + Math.min( + minTimeLine, + line.data.reduce( + (minTimeData, data) => + Math.min( + minTimeData, + new Date(data.states[0].last_changed).getTime() + ), + minTimeLine + ) + ), + minTimeAll + ); + + this._computedStartTime = new Date(minTimeAll); } } } diff --git a/src/components/chart/timeline-chart/timeline-controller.ts b/src/components/chart/timeline-chart/timeline-controller.ts index 5aeadc6e00..b6b1d88af3 100644 --- a/src/components/chart/timeline-chart/timeline-controller.ts +++ b/src/components/chart/timeline-chart/timeline-controller.ts @@ -205,7 +205,9 @@ export class TimelineController extends BarController { const y = vScale.getPixelForValue(this.index); - const xStart = iScale.getPixelForValue(data.start.getTime()); + const xStart = iScale.getPixelForValue( + Math.max(iScale.min, data.start.getTime()) + ); const xEnd = iScale.getPixelForValue(data.end.getTime()); const width = xEnd - xStart; diff --git a/src/components/chart/timeline-chart/timeline-scale.ts b/src/components/chart/timeline-chart/timeline-scale.ts index 8d5086dafc..e987456f8e 100644 --- a/src/components/chart/timeline-chart/timeline-scale.ts +++ b/src/components/chart/timeline-chart/timeline-scale.ts @@ -49,7 +49,7 @@ export class TimeLineScale extends TimeScale { max = isFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit); // Make sure that max is strictly higher than min (required by the lookup table) - this.min = Math.min(min, max - 1); - this.max = Math.max(min + 1, max); + this.min = adapter.parse(options.min, this) ?? Math.min(min, max - 1); + this.max = adapter.parse(options.max, this) ?? Math.max(min + 1, max); } } diff --git a/src/data/history.ts b/src/data/history.ts index baece3da37..e0fc8bd328 100644 --- a/src/data/history.ts +++ b/src/data/history.ts @@ -81,7 +81,7 @@ export interface EntityHistoryState { /** attributes */ a: { [key: string]: any }; /** last_changed; if set, also applies to lu */ - lc: number; + lc?: number; /** last_updated */ lu: number; } @@ -419,17 +419,37 @@ const BLANK_UNIT = " "; export const computeHistory = ( hass: HomeAssistant, stateHistory: HistoryStates, + entityIds: string[], localize: LocalizeFunc, sensorNumericalDeviceClasses: string[], splitDeviceClasses = false ): HistoryResult => { const lineChartDevices: { [unit: string]: HistoryStates } = {}; const timelineDevices: TimelineEntity[] = []; - if (!stateHistory) { + + const localStateHistory: HistoryStates = {}; + + // Create a limited history from stateObj if entity has no recorded history. + const allEntities = new Set([...entityIds, ...Object.keys(stateHistory)]); + allEntities.forEach((entity) => { + if (entity in stateHistory) { + localStateHistory[entity] = stateHistory[entity]; + } else if (hass.states[entity]) { + localStateHistory[entity] = [ + { + s: hass.states[entity].state, + a: hass.states[entity].attributes, + lu: new Date(hass.states[entity].last_updated).getTime() / 1000, + }, + ]; + } + }); + + if (!localStateHistory) { return { line: [], timeline: [] }; } - Object.keys(stateHistory).forEach((entityId) => { - const stateInfo = stateHistory[entityId]; + Object.keys(localStateHistory).forEach((entityId) => { + const stateInfo = localStateHistory[entityId]; if (stateInfo.length === 0) { return; } diff --git a/src/dialogs/more-info/ha-more-info-history.ts b/src/dialogs/more-info/ha-more-info-history.ts index 2adeb515c4..dc5c32f8a1 100644 --- a/src/dialogs/more-info/ha-more-info-history.ts +++ b/src/dialogs/more-info/ha-more-info-history.ts @@ -228,6 +228,7 @@ export class MoreInfoHistory extends LitElement { this._stateHistory = computeHistory( this.hass!, combinedHistory, + [this.entityId], this.hass!.localize, sensorNumericDeviceClasses ); diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts index dae3bae725..49797dda56 100644 --- a/src/panels/history/ha-panel-history.ts +++ b/src/panels/history/ha-panel-history.ts @@ -433,6 +433,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { this._statisticsHistory = computeHistory( this.hass, statsHistoryStates, + [], this.hass.localize, sensorNumericDeviceClasses, true @@ -472,6 +473,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) { this._stateHistory = computeHistory( this.hass, history, + entityIds, this.hass.localize, sensorNumericDeviceClasses, true diff --git a/src/panels/lovelace/cards/hui-history-graph-card.ts b/src/panels/lovelace/cards/hui-history-graph-card.ts index 1229ff7190..d8af4508b7 100644 --- a/src/panels/lovelace/cards/hui-history-graph-card.ts +++ b/src/panels/lovelace/cards/hui-history-graph-card.ts @@ -120,6 +120,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard { this._stateHistory = computeHistory( this.hass!, combinedHistory, + this._entityIds, this.hass!.localize, sensorNumericDeviceClasses, this._config?.split_device_classes From 4fb596357de18c6fdc4fec96790ca033735f1f53 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 28 Feb 2024 16:28:51 +0100 Subject: [PATCH 114/116] Add advanced options block to cloud remote connection (#19907) * Add advanced options block to cloud remote connection * Review --- .../config/cloud/account/cloud-remote-pref.ts | 74 +++++++++++++++---- src/translations/en.json | 7 +- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/panels/config/cloud/account/cloud-remote-pref.ts b/src/panels/config/cloud/account/cloud-remote-pref.ts index e94e50b56b..cd9d509f81 100644 --- a/src/panels/config/cloud/account/cloud-remote-pref.ts +++ b/src/panels/config/cloud/account/cloud-remote-pref.ts @@ -1,13 +1,16 @@ -import "@material/mwc-button"; import { mdiContentCopy, mdiHelpCircle } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../../common/dom/fire_event"; import { copyToClipboard } from "../../../../common/util/copy-clipboard"; import "../../../../components/ha-alert"; +import "../../../../components/ha-button"; import "../../../../components/ha-card"; +import "../../../../components/ha-expansion-panel"; +import "../../../../components/ha-settings-row"; import "../../../../components/ha-switch"; // eslint-disable-next-line +import { formatDate } from "../../../../common/datetime/format_date"; import type { HaSwitch } from "../../../../components/ha-switch"; import { CloudStatusLoggedIn, @@ -125,22 +128,60 @@ export class CloudRemotePref extends LitElement { >. - - - -
    -
    - - ${this.hass.localize( - "ui.panel.config.cloud.account.remote.certificate_info" + + > + + ${this.hass.localize( + "ui.panel.config.cloud.account.remote.external_activation" + )} + ${this.hass.localize( + "ui.panel.config.cloud.account.remote.external_activation_secondary" + )} + + + + ${this.hass.localize( + "ui.panel.config.cloud.account.remote.certificate_info" + )} + ${this.cloudStatus!.remote_certificate + ? this.hass.localize( + "ui.panel.config.cloud.account.remote.certificate_expire", + { + date: formatDate( + new Date( + this.cloudStatus.remote_certificate.expire_date + ), + this.hass.locale, + this.hass.config + ), + } + ) + : nothing} + + ${this.hass.localize( + "ui.panel.config.cloud.account.remote.more_info" + )} + + +
    `; @@ -243,6 +284,9 @@ export class CloudRemotePref extends LitElement { ha-formfield { margin-top: 8px; } + ha-expansion-panel { + margin-top: 8px; + } `; } } diff --git a/src/translations/en.json b/src/translations/en.json index 733d393c4c..5c385a9e32 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3508,7 +3508,12 @@ "instance_will_be_available": "Your instance will be available at your", "link_learn_how_it_works": "Learn how it works", "nabu_casa_url": "Nabu Casa URL", - "certificate_info": "Certificate info" + "advanced_options": "Advanced options", + "external_activation": "Allow external activation of remote control", + "external_activation_secondary": "Allows you to turn on remote control from your Nabu Casa account page, even if you're outside your local network", + "certificate_info": "Certificate info", + "certificate_expire": "Will be renewed at {date}", + "more_info": "More info" }, "alexa": { "title": "Alexa", From 3ef1110109ddfbdf21d23677e6b099969f2b2e18 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Wed, 28 Feb 2024 16:51:50 +0100 Subject: [PATCH 115/116] Bumped version to 20240228.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f0eef3c035..15c83c13a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20240207.1" +version = "20240228.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" From 52870616993c9676476d7cbb1b2115cfaad370ec Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 28 Feb 2024 11:07:26 -0500 Subject: [PATCH 116/116] Update script more info (#19899) * Update script more info * Fixes * Update styling * remarks * Always show cancel button --------- Co-authored-by: Bram Kragten --- src/components/ha-control-button.ts | 4 +- .../components/ha-more-info-state-header.ts | 6 +- src/dialogs/more-info/const.ts | 3 +- .../more-info/controls/more-info-script.ts | 183 ++++++++++++------ src/translations/en.json | 8 +- 5 files changed, 138 insertions(+), 66 deletions(-) diff --git a/src/components/ha-control-button.ts b/src/components/ha-control-button.ts index 1eee1286e6..58ea4f4400 100644 --- a/src/components/ha-control-button.ts +++ b/src/components/ha-control-button.ts @@ -127,9 +127,11 @@ export class HaControlButton extends LitElement { opacity 180ms ease-in-out; opacity: var(--control-button-background-opacity); } - .button ::slotted(*) { + .button { transition: color 180ms ease-in-out; color: var(--control-button-icon-color); + } + .button ::slotted(*) { pointer-events: none; } .button:disabled { diff --git a/src/dialogs/more-info/components/ha-more-info-state-header.ts b/src/dialogs/more-info/components/ha-more-info-state-header.ts index 4697868390..9432eb46b1 100644 --- a/src/dialogs/more-info/components/ha-more-info-state-header.ts +++ b/src/dialogs/more-info/components/ha-more-info-state-header.ts @@ -16,6 +16,8 @@ export class HaMoreInfoStateHeader extends LitElement { @property({ attribute: false }) public stateOverride?: string; + @property({ attribute: false }) public changedOverride?: number; + @state() private _absoluteTime = false; private _localizeState(): TemplateResult | string { @@ -50,13 +52,13 @@ export class HaMoreInfoStateHeader extends LitElement { ? html` ` : html` `} diff --git a/src/dialogs/more-info/const.ts b/src/dialogs/more-info/const.ts index b9fe72039a..e558910779 100644 --- a/src/dialogs/more-info/const.ts +++ b/src/dialogs/more-info/const.ts @@ -5,7 +5,7 @@ import { computeGroupDomain, GroupEntity } from "../../data/group"; import { CONTINUOUS_DOMAINS } from "../../data/logbook"; import { HomeAssistant } from "../../types"; -export const DOMAINS_NO_INFO = ["camera", "configurator", "script"]; +export const DOMAINS_NO_INFO = ["camera", "configurator"]; /** * Entity domains that should be editable *if* they have an id present; * {@see shouldShowEditIcon}. @@ -26,6 +26,7 @@ export const DOMAINS_WITH_NEW_MORE_INFO = [ "light", "lock", "siren", + "script", "switch", "valve", "water_heater", diff --git a/src/dialogs/more-info/controls/more-info-script.ts b/src/dialogs/more-info/controls/more-info-script.ts index a2fc2b1186..c530f05cf5 100644 --- a/src/dialogs/more-info/controls/more-info-script.ts +++ b/src/dialogs/more-info/controls/more-info-script.ts @@ -1,3 +1,4 @@ +import { mdiPlay, mdiStop } from "@mdi/js"; import "@material/mwc-button"; import { HassEntity } from "home-assistant-js-websocket"; import { @@ -8,28 +9,54 @@ import { nothing, PropertyValues, } from "lit"; -import { customElement, property } from "lit/decorators"; +import { customElement, property, state } from "lit/decorators"; import "../../../components/ha-relative-time"; import "../../../components/ha-service-control"; +import "../../../components/ha-control-button"; +import "../../../components/ha-control-button-group"; import "../../../components/entity/state-info"; import { HomeAssistant } from "../../../types"; import { canRun, ScriptEntity } from "../../../data/script"; import { isUnavailableState } from "../../../data/entity"; import { computeObjectId } from "../../../common/entity/compute_object_id"; +import { listenMediaQuery } from "../../../common/dom/media_query"; +import "../components/ha-more-info-state-header"; @customElement("more-info-script") class MoreInfoScript extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public stateObj?: HassEntity; + @property({ attribute: false }) public stateObj?: ScriptEntity; - private _scriptData: Record = {}; + @state() private _scriptData: Record = {}; + + @state() private narrow = false; + + private _unsubMediaQuery?: () => void; + + public connectedCallback(): void { + super.connectedCallback(); + this._unsubMediaQuery = listenMediaQuery( + "(max-width: 870px)", + (matches) => { + this.narrow = matches; + } + ); + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + if (this._unsubMediaQuery) { + this._unsubMediaQuery(); + this._unsubMediaQuery = undefined; + } + } protected render() { if (!this.hass || !this.stateObj) { return nothing; } - const stateObj = this.stateObj as ScriptEntity; + const stateObj = this.stateObj; const fields = this.hass.services.script[computeObjectId(this.stateObj.entity_id)] @@ -37,64 +64,74 @@ class MoreInfoScript extends LitElement { const hasFields = fields && Object.keys(fields).length > 0; + const current = stateObj.attributes.current || 0; + const isQueued = stateObj.attributes.mode === "queued"; + const isParallel = stateObj.attributes.mode === "parallel"; + const hasQueue = isQueued && current > 1; + return html` -
    - - ${stateObj.state === "on" - ? html` - ${stateObj.attributes.mode !== "single" && - (stateObj.attributes.current || 0) > 0 - ? this.hass.localize("ui.card.script.cancel_multiple", { - number: stateObj.attributes.current, - }) - : this.hass.localize("ui.card.script.cancel")} - ` - : nothing} - ${stateObj.state === "off" || stateObj.attributes.max - ? html` - ${this.hass!.localize("ui.card.script.run")} - ` - : nothing} + 0 + ? isParallel && current > 1 + ? this.hass.localize("ui.card.script.running_parallel", { + active: current, + }) + : this.hass.localize("ui.card.script.running_single") + : this.hass.localize("ui.card.script.idle")} + .changedOverride=${this.stateObj.attributes.last_triggered || 0} + > + +
    + ${hasQueue + ? html` + ${this.hass.localize("ui.card.script.running_queued", { + queued: current - 1, + })} + ` + : ""}
    ${hasFields ? html` - +
    +
    + ${this.hass.localize("ui.card.script.run_script")} +
    + +
    ` : nothing} -
    -
    -
    - ${this.hass.localize( - "ui.dialogs.more_info_control.script.last_triggered" - )}: -
    - ${this.stateObj.attributes.last_triggered - ? html` - - ` - : this.hass.localize("ui.components.relative_time.never")} -
    + + + + ${(isQueued || isParallel) && current > 1 + ? this.hass.localize("ui.card.script.cancel_all") + : this.hass.localize("ui.card.script.cancel")} + + + + ${this.hass!.localize("ui.card.script.run")} + + `; } @@ -139,17 +176,41 @@ class MoreInfoScript extends LitElement { this._scriptData = { ...this._scriptData, ...ev.detail.value }; } + private _canRun() { + if ( + canRun(this.stateObj!) || + // Restart can also always runs. Just cancels other run. + this.stateObj!.attributes.mode === "restart" + ) { + return true; + } + return false; + } + static get styles(): CSSResultGroup { return css` - .flex { - display: flex; - justify-content: space-between; + .queue { + visibility: hidden; + color: var(--secondary-text-color); + text-align: center; + margin-bottom: 16px; + height: 21px; + } + .queue.has-queue { + visibility: visible; + } + .fields { + padding: 16px; + border: 1px solid var(--divider-color); + border-radius: 8px; margin-bottom: 16px; } - hr { - border-color: var(--divider-color); - border-bottom: none; - margin: 16px 0; + .fields .title { + font-weight: bold; + } + ha-control-button ha-svg-icon { + z-index: -1; + margin-right: 4px; } ha-service-control { --service-control-padding: 0; diff --git a/src/translations/en.json b/src/translations/en.json index 5c385a9e32..24f63e86a3 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -218,8 +218,14 @@ }, "script": { "run": "[%key:ui::card::service::run%]", + "running_single": "Running…", + "running_queued": "{queued} queued", + "running_parallel": "{active} Running…", "cancel": "Cancel", - "cancel_multiple": "Cancel {number}" + "cancel_multiple": "Cancel {number}", + "cancel_all": "Cancel all", + "idle": "Idle", + "run_script": "Run script" }, "service": { "run": "Run"