diff --git a/pyproject.toml b/pyproject.toml index f19e51af4f..c78a2c67f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20240402.1" +version = "20240402.2" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/components/ha-filter-blueprints.ts b/src/components/ha-filter-blueprints.ts index c20dc197bf..2b48e27e54 100644 --- a/src/components/ha-filter-blueprints.ts +++ b/src/components/ha-filter-blueprints.ts @@ -1,12 +1,13 @@ import { SelectedDetail } from "@material/mwc-list"; import "@material/mwc-menu/mwc-menu-surface"; +import { mdiFilterVariantRemove } 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 { findRelated, RelatedResult } from "../data/search"; -import type { HomeAssistant } from "../types"; -import { haStyleScrollbar } from "../resources/styles"; import { Blueprints, fetchBlueprints } from "../data/blueprint"; +import { findRelated, RelatedResult } from "../data/search"; +import { haStyleScrollbar } from "../resources/styles"; +import type { HomeAssistant } from "../types"; @customElement("ha-filter-blueprints") export class HaFilterBlueprints extends LitElement { @@ -35,7 +36,11 @@ export class HaFilterBlueprints extends LitElement {
${this.hass.localize("ui.panel.config.blueprint.caption")} ${this.value?.length - ? html`
${this.value?.length}
` + ? html`
${this.value?.length}
+ ` : nothing}
${this._blueprints && this._shouldRender @@ -128,6 +133,15 @@ export class HaFilterBlueprints extends LitElement { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -147,6 +161,10 @@ export class HaFilterBlueprints extends LitElement { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; diff --git a/src/components/ha-filter-categories.ts b/src/components/ha-filter-categories.ts index fdaa8d481a..40dac92a85 100644 --- a/src/components/ha-filter-categories.ts +++ b/src/components/ha-filter-categories.ts @@ -2,6 +2,7 @@ import { ActionDetail, SelectedDetail } from "@material/mwc-list"; import { mdiDelete, mdiDotsVertical, + mdiFilterVariantRemove, mdiPencil, mdiPlus, mdiTag, @@ -68,7 +69,11 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) {
${this.hass.localize("ui.panel.config.category.caption")} ${this.value?.length - ? html`
${this.value?.length}
` + ? html`
${this.value?.length}
+ ` : nothing}
${this._shouldRender @@ -254,6 +259,15 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -274,6 +288,10 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; diff --git a/src/components/ha-filter-devices.ts b/src/components/ha-filter-devices.ts index 4b9f2bebc4..97f17f9c83 100644 --- a/src/components/ha-filter-devices.ts +++ b/src/components/ha-filter-devices.ts @@ -1,3 +1,4 @@ +import { mdiFilterVariantRemove } from "@mdi/js"; import { css, CSSResultGroup, @@ -13,10 +14,11 @@ import { stringCompare } from "../common/string/compare"; import { computeDeviceName } from "../data/device_registry"; import { findRelated, RelatedResult } from "../data/search"; import { haStyleScrollbar } from "../resources/styles"; -import type { HomeAssistant } from "../types"; -import "./ha-expansion-panel"; -import "./ha-check-list-item"; import { loadVirtualizer } from "../resources/virtualizer"; +import type { HomeAssistant } from "../types"; +import "./ha-check-list-item"; +import "./ha-expansion-panel"; +import "./search-input-outlined"; @customElement("ha-filter-devices") export class HaFilterDevices extends LitElement { @@ -32,6 +34,8 @@ export class HaFilterDevices extends LitElement { @state() private _shouldRender = false; + @state() private _filter?: string; + public willUpdate(properties: PropertyValues) { super.willUpdate(properties); @@ -51,19 +55,33 @@ export class HaFilterDevices extends LitElement {
${this.hass.localize("ui.panel.config.devices.caption")} ${this.value?.length - ? html`
${this.value?.length}
` + ? html`
${this.value?.length}
+ ` : nothing}
${this._shouldRender - ? html` - - - ` + + + + + ` : nothing} `; @@ -72,12 +90,14 @@ export class HaFilterDevices extends LitElement { private _keyFunction = (device) => device?.id; private _renderItem = (device) => - html` - ${computeDeviceName(device, this.hass)} - `; + !device + ? nothing + : html` + ${computeDeviceName(device, this.hass)} + `; private _handleItemClick(ev) { const listItem = ev.target.closest("ha-check-list-item"); @@ -99,7 +119,7 @@ export class HaFilterDevices extends LitElement { setTimeout(() => { if (!this.expanded) return; this.renderRoot.querySelector("mwc-list")!.style.height = - `${this.clientHeight - 49}px`; + `${this.clientHeight - 49 - 32}px`; // 32px is the height of the search input }, 300); } } @@ -112,16 +132,28 @@ export class HaFilterDevices extends LitElement { this.expanded = ev.detail.expanded; } - private _devices = memoizeOne((devices: HomeAssistant["devices"], _value) => { - const values = Object.values(devices); - return values.sort((a, b) => - stringCompare( - a.name_by_user || a.name || "", - b.name_by_user || b.name || "", - this.hass.locale.language - ) - ); - }); + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value.toLowerCase(); + } + + private _devices = memoizeOne( + (devices: HomeAssistant["devices"], filter: string, _value) => { + const values = Object.values(devices); + return values + .filter( + (device) => + !filter || + computeDeviceName(device, this.hass).toLowerCase().includes(filter) + ) + .sort((a, b) => + stringCompare( + computeDeviceName(a, this.hass), + computeDeviceName(b, this.hass), + this.hass.locale.language + ) + ); + } + ); private async _findRelated() { const relatedPromises: Promise[] = []; @@ -158,6 +190,15 @@ export class HaFilterDevices extends LitElement { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -178,6 +219,10 @@ export class HaFilterDevices extends LitElement { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; @@ -197,6 +242,10 @@ export class HaFilterDevices extends LitElement { ha-check-list-item { width: 100%; } + search-input-outlined { + display: block; + padding: 0 8px; + } `, ]; } diff --git a/src/components/ha-filter-entities.ts b/src/components/ha-filter-entities.ts index 2cffd99456..585d35a527 100644 --- a/src/components/ha-filter-entities.ts +++ b/src/components/ha-filter-entities.ts @@ -1,3 +1,4 @@ +import { mdiFilterVariantRemove } from "@mdi/js"; import { css, CSSResultGroup, @@ -14,10 +15,11 @@ import { computeStateName } from "../common/entity/compute_state_name"; import { stringCompare } from "../common/string/compare"; import { findRelated, RelatedResult } from "../data/search"; import { haStyleScrollbar } from "../resources/styles"; -import type { HomeAssistant } from "../types"; -import "./ha-state-icon"; -import "./ha-check-list-item"; import { loadVirtualizer } from "../resources/virtualizer"; +import type { HomeAssistant } from "../types"; +import "./ha-check-list-item"; +import "./ha-state-icon"; +import "./search-input-outlined"; @customElement("ha-filter-entities") export class HaFilterEntities extends LitElement { @@ -33,6 +35,8 @@ export class HaFilterEntities extends LitElement { @state() private _shouldRender = false; + @state() private _filter?: string; + public willUpdate(properties: PropertyValues) { super.willUpdate(properties); @@ -52,16 +56,27 @@ export class HaFilterEntities extends LitElement {
${this.hass.localize("ui.panel.config.entities.caption")} ${this.value?.length - ? html`
${this.value?.length}
` + ? html`
${this.value?.length}
+ ` : nothing}
${this._shouldRender ? html` + + { if (!this.expanded) return; this.renderRoot.querySelector("mwc-list")!.style.height = - `${this.clientHeight - 49}px`; + `${this.clientHeight - 49 - 32}px`; // 32px is the height of the search input }, 300); } } @@ -89,18 +104,20 @@ export class HaFilterEntities extends LitElement { private _keyFunction = (entity) => entity?.entity_id; private _renderItem = (entity) => - html` - - ${computeStateName(entity)} - `; + !entity + ? nothing + : html` + + ${computeStateName(entity)} + `; private _handleItemClick(ev) { const listItem = ev.target.closest("ha-check-list-item"); @@ -125,12 +142,27 @@ export class HaFilterEntities extends LitElement { this.expanded = ev.detail.expanded; } + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value.toLowerCase(); + } + private _entities = memoizeOne( - (states: HomeAssistant["states"], type: this["type"], _value) => { + ( + states: HomeAssistant["states"], + type: this["type"], + filter: string, + _value + ) => { const values = Object.values(states); return values .filter( - (entityState) => !type || computeStateDomain(entityState) !== type + (entityState) => + (!type || computeStateDomain(entityState) !== type) && + (!filter || + entityState.entity_id.toLowerCase().includes(filter) || + entityState.attributes.friendly_name + ?.toLowerCase() + .includes(filter)) ) .sort((a, b) => stringCompare( @@ -177,6 +209,15 @@ export class HaFilterEntities extends LitElement { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -196,6 +237,10 @@ export class HaFilterEntities extends LitElement { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; @@ -216,6 +261,10 @@ export class HaFilterEntities extends LitElement { --mdc-list-item-graphic-margin: 16px; width: 100%; } + search-input-outlined { + display: block; + padding: 0 8px; + } `, ]; } diff --git a/src/components/ha-filter-floor-areas.ts b/src/components/ha-filter-floor-areas.ts index 6960621fb3..ba983b7349 100644 --- a/src/components/ha-filter-floor-areas.ts +++ b/src/components/ha-filter-floor-areas.ts @@ -1,5 +1,5 @@ import "@material/mwc-menu/mwc-menu-surface"; -import { mdiTextureBox } from "@mdi/js"; +import { mdiFilterVariantRemove, mdiTextureBox } from "@mdi/js"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; @@ -53,9 +53,13 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) { ${this.hass.localize("ui.panel.config.areas.caption")} ${this.value?.areas?.length || this.value?.floors?.length ? html`
- ${(this.value?.areas?.length || 0) + - (this.value?.floors?.length || 0)} -
` + ${(this.value?.areas?.length || 0) + + (this.value?.floors?.length || 0)} + + ` : nothing} ${this._shouldRender @@ -238,6 +242,15 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -257,6 +270,10 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; diff --git a/src/components/ha-filter-integrations.ts b/src/components/ha-filter-integrations.ts index 5f8b1224b5..2f8b6f3cf0 100644 --- a/src/components/ha-filter-integrations.ts +++ b/src/components/ha-filter-integrations.ts @@ -1,4 +1,5 @@ import { SelectedDetail } from "@material/mwc-list"; +import { mdiFilterVariantRemove } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; @@ -38,7 +39,11 @@ export class HaFilterIntegrations extends LitElement {
${this.hass.localize("ui.panel.config.integrations.caption")} ${this.value?.length - ? html`
${this.value?.length}
` + ? html`
${this.value?.length}
+ ` : nothing}
${this._manifests && this._shouldRender @@ -142,6 +147,15 @@ export class HaFilterIntegrations extends LitElement { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -161,6 +175,10 @@ export class HaFilterIntegrations extends LitElement { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; diff --git a/src/components/ha-filter-labels.ts b/src/components/ha-filter-labels.ts index 43c3c10098..eef52f52de 100644 --- a/src/components/ha-filter-labels.ts +++ b/src/components/ha-filter-labels.ts @@ -1,19 +1,18 @@ import { SelectedDetail } from "@material/mwc-list"; import "@material/mwc-menu/mwc-menu-surface"; -import { mdiPlus } from "@mdi/js"; +import { mdiCog, mdiFilterVariantRemove } from "@mdi/js"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { CSSResultGroup, LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { repeat } from "lit/directives/repeat"; import { computeCssColor } from "../common/color/compute-color"; import { fireEvent } from "../common/dom/fire_event"; +import { navigate } from "../common/navigate"; import { LabelRegistryEntry, - createLabelRegistryEntry, subscribeLabelRegistry, } from "../data/label_registry"; import { SubscribeMixin } from "../mixins/subscribe-mixin"; -import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail"; import { haStyleScrollbar } from "../resources/styles"; import type { HomeAssistant } from "../types"; import "./ha-check-list-item"; @@ -54,7 +53,11 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) {
${this.hass.localize("ui.panel.config.labels.caption")} ${this.value?.length - ? html`
${this.value?.length}
` + ? html`
${this.value?.length}
+ ` : nothing}
${this._shouldRender @@ -95,11 +98,11 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) { ${this.expanded ? html` - - ${this.hass.localize("ui.panel.config.labels.add_label")} + + ${this.hass.localize("ui.panel.config.labels.manage_labels")} ` : nothing} `; @@ -115,10 +118,8 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) { } } - private _addLabel() { - showLabelDetailDialog(this, { - createEntry: (values) => createLabelRegistryEntry(this.hass, values), - }); + private _manageLabels() { + navigate("/config/labels"); } private _expandedWillChange(ev) { @@ -153,6 +154,15 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -173,6 +183,10 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; diff --git a/src/components/ha-filter-states.ts b/src/components/ha-filter-states.ts index 71451460b0..b3eb0fc1da 100644 --- a/src/components/ha-filter-states.ts +++ b/src/components/ha-filter-states.ts @@ -1,11 +1,12 @@ import { SelectedDetail } from "@material/mwc-list"; +import { mdiFilterVariantRemove } 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 { haStyleScrollbar } from "../resources/styles"; import type { HomeAssistant } from "../types"; -import "./ha-expansion-panel"; import "./ha-check-list-item"; +import "./ha-expansion-panel"; import "./ha-icon"; @customElement("ha-filter-states") @@ -43,7 +44,11 @@ export class HaFilterStates extends LitElement {
${this.label} ${this.value?.length - ? html`
${this.value?.length}
` + ? html`
${this.value?.length}
+ ` : nothing}
${this._shouldRender @@ -118,6 +123,15 @@ export class HaFilterStates extends LitElement { }); } + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + static get styles(): CSSResultGroup { return [ haStyleScrollbar, @@ -137,6 +151,10 @@ export class HaFilterStates extends LitElement { display: flex; align-items: center; } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } .badge { display: inline-block; margin-left: 8px; diff --git a/src/components/search-input-outlined.ts b/src/components/search-input-outlined.ts index f949323cac..5e40df35c9 100644 --- a/src/components/search-input-outlined.ts +++ b/src/components/search-input-outlined.ts @@ -1,5 +1,12 @@ -import { mdiMagnify } from "@mdi/js"; -import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit"; +import { mdiClose, mdiMagnify } from "@mdi/js"; +import { + CSSResultGroup, + LitElement, + TemplateResult, + css, + html, + nothing, +} from "lit"; import { customElement, property, query } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { HomeAssistant } from "../types"; @@ -54,6 +61,15 @@ class SearchInputOutlined extends LitElement { .path=${mdiMagnify} > + ${this.filter + ? html` + ` + : nothing} `; } @@ -66,12 +82,17 @@ class SearchInputOutlined extends LitElement { this._filterChanged(e.target.value); } + private async _clearSearch() { + this._filterChanged(""); + } + static get styles(): CSSResultGroup { return css` :host { display: inline-flex; /* For iOS */ z-index: 0; + --mdc-icon-button-size: 24px; } ha-outlined-text-field { display: block; diff --git a/src/layouts/hass-tabs-subpage-data-table.ts b/src/layouts/hass-tabs-subpage-data-table.ts index 53a60e37e3..57036a9ab4 100644 --- a/src/layouts/hass-tabs-subpage-data-table.ts +++ b/src/layouts/hass-tabs-subpage-data-table.ts @@ -368,14 +368,16 @@ export class HaTabsSubpageDataTable extends LitElement { "ui.components.subpage-data-table.filters" )} - + ${this.filters + ? html`` + : nothing}
- + ${this.filters + ? html`` + : nothing}
diff --git a/src/panels/config/areas/ha-config-areas-dashboard.ts b/src/panels/config/areas/ha-config-areas-dashboard.ts index 875e13367b..12584b4f22 100644 --- a/src/panels/config/areas/ha-config-areas-dashboard.ts +++ b/src/panels/config/areas/ha-config-areas-dashboard.ts @@ -271,7 +271,14 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) { ? html`` : ""}
-

${area.name}

+
+ ${area.name} + +
${formatListWithAnds( @@ -305,6 +312,16 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) { loadAreaRegistryDetailDialog(); } + private _openAreaDetails(ev) { + ev.preventDefault(); + const area = ev.currentTarget.area; + showAreaRegistryDetailDialog(this, { + entry: area, + updateEntry: async (values) => + updateAreaRegistryEntry(this.hass!, area.area_id, values), + }); + } + private async _areaMoved(ev) { const areasAndFloors = this._processAreas( this.hass.areas, @@ -469,8 +486,10 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) { min-height: 16px; color: var(--secondary-text-color); } - .floor { - --primary-color: var(--secondary-text-color); + .card-header { + display: flex; + justify-content: space-between; + align-items: center; } .warning { color: var(--error-color); diff --git a/src/panels/lovelace/common/generate-lovelace-config.ts b/src/panels/lovelace/common/generate-lovelace-config.ts index 8af49cabdc..819001bb53 100644 --- a/src/panels/lovelace/common/generate-lovelace-config.ts +++ b/src/panels/lovelace/common/generate-lovelace-config.ts @@ -35,6 +35,7 @@ import { ButtonsHeaderFooterConfig } from "../header-footer/types"; const HIDE_DOMAIN = new Set([ "automation", "configurator", + "conversation", "device_tracker", "geo_location", "persistent_notification", diff --git a/src/panels/lovelace/common/validate-condition.ts b/src/panels/lovelace/common/validate-condition.ts index 00294ff9fe..908d5719dd 100644 --- a/src/panels/lovelace/common/validate-condition.ts +++ b/src/panels/lovelace/common/validate-condition.ts @@ -58,18 +58,12 @@ export interface AndCondition extends BaseCondition { function getValueFromEntityId( hass: HomeAssistant, - value: string | string[] -): string | string[] { - if ( - typeof value === "string" && - isValidEntityId(value) && - hass.states[value] - ) { - value = hass.states[value]?.state; - } else if (Array.isArray(value)) { - value = value.map((v) => getValueFromEntityId(hass, v) as string); + value: string +): string | undefined { + if (isValidEntityId(value) && hass.states[value]) { + return hass.states[value]?.state; } - return value; + return undefined; } function checkStateCondition( @@ -83,8 +77,17 @@ function checkStateCondition( let value = condition.state ?? condition.state_not; // Handle entity_id, UI should be updated for conditionnal card (filters does not have UI for now) - if (Array.isArray(value) || typeof value === "string") { - value = getValueFromEntityId(hass, value); + if (Array.isArray(value)) { + const entityValues = value + .map((v) => getValueFromEntityId(hass, v)) + .filter((v): v is string => v !== undefined); + value = [...value, ...entityValues]; + } else if (typeof value === "string") { + const entityValue = getValueFromEntityId(hass, value); + value = [value]; + if (entityValue) { + value.push(entityValue); + } } return condition.state != null @@ -103,10 +106,10 @@ function checkStateNumericCondition( // Handle entity_id, UI should be updated for conditionnal card (filters does not have UI for now) if (typeof above === "string") { - above = getValueFromEntityId(hass, above) as string; + above = getValueFromEntityId(hass, above) ?? above; } if (typeof below === "string") { - below = getValueFromEntityId(hass, below) as string; + below = getValueFromEntityId(hass, below) ?? below; } const numericState = Number(state); diff --git a/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts index 0a826b11b0..f2b2320283 100644 --- a/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts +++ b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts @@ -172,12 +172,14 @@ class DialogDashboardStrategyEditor extends LitElement { `; } - private _takeControl() { + private _takeControl(ev) { + ev.stopPropagation(); this._params!.takeControl(); this.closeDialog(); } - private _showRawConfigEditor() { + private _showRawConfigEditor(ev) { + ev.stopPropagation(); this._params!.showRawConfigEditor(); this.closeDialog(); } diff --git a/src/panels/my/ha-panel-my.ts b/src/panels/my/ha-panel-my.ts index 100eeddf59..e5a5e2c327 100644 --- a/src/panels/my/ha-panel-my.ts +++ b/src/panels/my/ha-panel-my.ts @@ -116,6 +116,9 @@ export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({ entities: { redirect: "/config/entities", }, + labels: { + redirect: "/config/labels", + }, energy: { component: "energy", redirect: "/energy", diff --git a/src/translations/en.json b/src/translations/en.json index 2f0ed5d46c..f41492dce9 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1962,6 +1962,7 @@ "color": "Color" }, "add_label": "Add label", + "manage_labels": "Manage labels", "no_labels": "You don't have any labels", "introduction": "Labels can help you organize your areas, devices and entities. They can be used to filter in the UI, or use them as a target in automations.", "introduction2": "Go to the area, device or entity you want to add a label to, and click on the edit button to assign labels to them.",