diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts index 7a16c1ec10..b2610c109c 100644 --- a/src/data/device_registry.ts +++ b/src/data/device_registry.ts @@ -1,5 +1,6 @@ import { Connection, createCollection } from "home-assistant-js-websocket"; import { computeStateName } from "../common/entity/compute_state_name"; +import { caseInsensitiveStringCompare } from "../common/string/compare"; import { debounce } from "../common/util/debounce"; import { HomeAssistant } from "../types"; import { EntityRegistryEntry } from "./entity_registry"; @@ -99,3 +100,8 @@ export const subscribeDeviceRegistry = ( conn, onChange ); + +export const sortDeviceRegistryByName = (entries: DeviceRegistryEntry[]) => + entries.sort((entry1, entry2) => + caseInsensitiveStringCompare(entry1.name || "", entry2.name || "") + ); diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts index 84fdbccec5..83fcc601b5 100644 --- a/src/data/entity_registry.ts +++ b/src/data/entity_registry.ts @@ -1,6 +1,7 @@ import { Connection, createCollection } from "home-assistant-js-websocket"; import { Store } from "home-assistant-js-websocket/dist/store"; import { computeStateName } from "../common/entity/compute_state_name"; +import { caseInsensitiveStringCompare } from "../common/string/compare"; import { debounce } from "../common/util/debounce"; import { HomeAssistant } from "../types"; @@ -133,3 +134,8 @@ export const subscribeEntityRegistry = ( conn, onChange ); + +export const sortEntityRegistryByName = (entries: EntityRegistryEntry[]) => + entries.sort((entry1, entry2) => + caseInsensitiveStringCompare(entry1.name || "", entry2.name || "") + ); diff --git a/src/panels/config/areas/ha-config-area-page.ts b/src/panels/config/areas/ha-config-area-page.ts index 285d18bba7..0a971181cb 100644 --- a/src/panels/config/areas/ha-config-area-page.ts +++ b/src/panels/config/areas/ha-config-area-page.ts @@ -1,13 +1,17 @@ import "@material/mwc-button"; +import { mdiImagePlus, mdiPencil } from "@mdi/js"; import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item-body"; -import { mdiImagePlus, mdiPencil } from "@mdi/js"; +import { HassEntity } from "home-assistant-js-websocket/dist/types"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; +import { computeDomain } from "../../../common/entity/compute_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; +import { caseInsensitiveStringCompare } from "../../../common/string/compare"; +import { groupBy } from "../../../common/util/group-by"; import { afterNextRender } from "../../../common/util/render-status"; import "../../../components/ha-card"; import "../../../components/ha-icon-button"; @@ -17,14 +21,19 @@ import { deleteAreaRegistryEntry, updateAreaRegistryEntry, } from "../../../data/area_registry"; +import { AutomationEntity } from "../../../data/automation"; import { computeDeviceName, DeviceRegistryEntry, + sortDeviceRegistryByName, } from "../../../data/device_registry"; import { computeEntityRegistryName, EntityRegistryEntry, + sortEntityRegistryByName, } from "../../../data/entity_registry"; +import { SceneEntity } from "../../../data/scene"; +import { ScriptEntity } from "../../../data/script"; import { findRelated, RelatedResult } from "../../../data/search"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../resources/styles"; @@ -35,11 +44,11 @@ import { loadAreaRegistryDetailDialog, showAreaRegistryDetailDialog, } from "./show-dialog-area-registry-detail"; -import { computeDomain } from "../../../common/entity/compute_domain"; -import { SceneEntity } from "../../../data/scene"; -import { ScriptEntity } from "../../../data/script"; -import { AutomationEntity } from "../../../data/automation"; -import { groupBy } from "../../../common/util/group-by"; + +declare type NameAndEntity = { + name: string; + entity: EntityType; +}; @customElement("ha-config-area-page") class HaConfigAreaPage extends LitElement { @@ -136,10 +145,59 @@ class HaConfigAreaPage extends LitElement { this.entities ); - const grouped = groupBy(entities, (entity) => + // Pre-compute the entity and device names, so we can sort by them + if (devices) { + devices.forEach((entry) => { + entry.name = computeDeviceName(entry, this.hass); + }); + sortDeviceRegistryByName(devices); + } + if (entities) { + entities.forEach((entry) => { + entry.name = computeEntityRegistryName(this.hass, entry); + }); + sortEntityRegistryByName(entities); + } + + // Group entities by domain + const groupedEntities = groupBy(entities, (entity) => computeDomain(entity.entity_id) ); + // Pre-compute the name also for the grouped and related entities so we can sort by them + let groupedAutomations: NameAndEntity[] = []; + let groupedScenes: NameAndEntity[] = []; + let groupedScripts: NameAndEntity[] = []; + let relatedAutomations: NameAndEntity[] = []; + let relatedScenes: NameAndEntity[] = []; + let relatedScripts: NameAndEntity[] = []; + + if (isComponentLoaded(this.hass, "automation")) { + ({ + groupedEntities: groupedAutomations, + relatedEntities: relatedAutomations, + } = this._prepareEntities( + groupedEntities.automation, + this._related?.automation + )); + } + + if (isComponentLoaded(this.hass, "scene")) { + ({ groupedEntities: groupedScenes, relatedEntities: relatedScenes } = + this._prepareEntities( + groupedEntities.scene, + this._related?.scene + )); + } + + if (isComponentLoaded(this.hass, "script")) { + ({ groupedEntities: groupedScripts, relatedEntities: relatedScripts } = + this._prepareEntities( + groupedEntities.script, + this._related?.script + )); + } + return html` - - ${computeDeviceName(device, this.hass)} - + ${device.name} @@ -240,9 +296,7 @@ class HaConfigAreaPage extends LitElement { @click=${this._openEntity} .entity=${entity} > - - ${computeEntityRegistryName(this.hass, entity)} - + ${entity.name} ` @@ -264,43 +318,33 @@ class HaConfigAreaPage extends LitElement { "ui.panel.config.devices.automation.automations" )} > - ${grouped.automation?.length + ${groupedAutomations?.length ? html`

${this.hass.localize( "ui.panel.config.areas.assigned_to_area" )}:

- ${grouped.automation.map((entity) => { - const entityState = this.hass.states[ - entity.entity_id - ] as AutomationEntity | undefined; - return entityState - ? this._renderAutomation(entityState) - : ""; - })}` + ${groupedAutomations.map((automation) => + this._renderAutomation( + automation.name, + automation.entity + ) + )}` : ""} - ${this._related?.automation?.filter( - (entityId) => - !grouped.automation?.find( - (entity) => entity.entity_id === entityId - ) - ).length + ${relatedAutomations?.length ? html`

${this.hass.localize( "ui.panel.config.areas.targeting_area" )}:

- ${this._related.automation.map((scene) => { - const entityState = this.hass.states[scene] as - | AutomationEntity - | undefined; - return entityState - ? this._renderAutomation(entityState) - : ""; - })}` + ${relatedAutomations.map((automation) => + this._renderAutomation( + automation.name, + automation.entity + ) + )}` : ""} - ${!grouped.automation?.length && - !this._related?.automation?.length + ${!groupedAutomations?.length && !relatedAutomations?.length ? html` ${this.hass.localize( @@ -321,39 +365,27 @@ class HaConfigAreaPage extends LitElement { "ui.panel.config.devices.scene.scenes" )} > - ${grouped.scene?.length + ${groupedScenes?.length ? html`

${this.hass.localize( "ui.panel.config.areas.assigned_to_area" )}:

- ${grouped.scene.map((entity) => { - const entityState = - this.hass.states[entity.entity_id]; - return entityState - ? this._renderScene(entityState) - : ""; - })}` + ${groupedScenes.map((scene) => + this._renderScene(scene.name, scene.entity) + )}` : ""} - ${this._related?.scene?.filter( - (entityId) => - !grouped.scene?.find( - (entity) => entity.entity_id === entityId - ) - ).length + ${relatedScenes?.length ? html`

${this.hass.localize( "ui.panel.config.areas.targeting_area" )}:

- ${this._related.scene.map((scene) => { - const entityState = this.hass.states[scene]; - return entityState - ? this._renderScene(entityState) - : ""; - })}` + ${relatedScenes.map((scene) => + this._renderScene(scene.name, scene.entity) + )}` : ""} - ${!grouped.scene?.length && !this._related?.scene?.length + ${!groupedScenes?.length && !relatedScenes?.length ? html` ${this.hass.localize( @@ -372,42 +404,27 @@ class HaConfigAreaPage extends LitElement { "ui.panel.config.devices.script.scripts" )} > - ${grouped.script?.length + ${groupedScripts?.length ? html`

${this.hass.localize( "ui.panel.config.areas.assigned_to_area" )}:

- ${grouped.script.map((entity) => { - const entityState = this.hass.states[ - entity.entity_id - ] as ScriptEntity | undefined; - return entityState - ? this._renderScript(entityState) - : ""; - })}` + ${groupedScripts.map((script) => + this._renderScript(script.name, script.entity) + )}` : ""} - ${this._related?.script?.filter( - (entityId) => - !grouped.script?.find( - (entity) => entity.entity_id === entityId - ) - ).length + ${relatedScripts?.length ? html`

${this.hass.localize( "ui.panel.config.areas.targeting_area" )}:

- ${this._related.script.map((scene) => { - const entityState = this.hass.states[scene] as - | ScriptEntity - | undefined; - return entityState - ? this._renderScript(entityState) - : ""; - })}` + ${relatedScripts.map((script) => + this._renderScript(script.name, script.entity) + )}` : ""} - ${!grouped.script?.length && !this._related?.script?.length + ${!groupedScripts?.length && !relatedScripts?.length ? html` ${this.hass.localize( @@ -425,7 +442,51 @@ class HaConfigAreaPage extends LitElement { `; } - private _renderScene(entityState: SceneEntity) { + private _prepareEntities( + entries?: EntityRegistryEntry[], + relatedEntityIds?: string[] + ): { + groupedEntities: NameAndEntity[]; + relatedEntities: NameAndEntity[]; + } { + const groupedEntities: NameAndEntity[] = []; + const relatedEntities: NameAndEntity[] = []; + + if (entries?.length) { + entries.forEach((entity) => { + const entityState = this.hass.states[ + entity.entity_id + ] as unknown as EntityType; + if (entityState) { + groupedEntities.push({ + name: computeStateName(entityState), + entity: entityState, + }); + } + }); + groupedEntities.sort((entry1, entry2) => + caseInsensitiveStringCompare(entry1.name!, entry2.name!) + ); + } + if (relatedEntityIds?.length) { + relatedEntityIds.forEach((entity) => { + const entityState = this.hass.states[entity] as EntityType; + if (entityState) { + relatedEntities.push({ + name: entityState ? computeStateName(entityState) : "", + entity: entityState, + }); + } + }); + relatedEntities.sort((entry1, entry2) => + caseInsensitiveStringCompare(entry1.name!, entry2.name!) + ); + } + + return { groupedEntities, relatedEntities }; + } + + private _renderScene(name: string, entityState: SceneEntity) { return html`
- ${computeStateName(entityState)} + ${name} @@ -449,7 +510,7 @@ class HaConfigAreaPage extends LitElement {
`; } - private _renderAutomation(entityState: AutomationEntity) { + private _renderAutomation(name: string, entityState: AutomationEntity) { return html`
- ${computeStateName(entityState)} + ${name} @@ -473,10 +534,10 @@ class HaConfigAreaPage extends LitElement {
`; } - private _renderScript(entityState: ScriptEntity) { + private _renderScript(name: string, entityState: ScriptEntity) { return html` - ${computeStateName(entityState)} + ${name} `;