From 6d3d194f42c612163f710b5fa7f4875d3eb5637b Mon Sep 17 00:00:00 2001 From: Zack Date: Fri, 15 Apr 2022 23:06:17 -0500 Subject: [PATCH] Add area, device and entity registry --- src/data/quick-bar.ts | 91 +++++++-------- src/dialogs/quick-bar/ha-quick-bar.ts | 161 +++++++++++++++++--------- 2 files changed, 151 insertions(+), 101 deletions(-) diff --git a/src/data/quick-bar.ts b/src/data/quick-bar.ts index eb91f3a604..bad546d885 100644 --- a/src/data/quick-bar.ts +++ b/src/data/quick-bar.ts @@ -1,44 +1,43 @@ import { - mdiServerNetwork, - mdiNavigationVariantOutline, mdiEarth, + mdiNavigationVariantOutline, mdiReload, + mdiServerNetwork, } from "@mdi/js"; import { canShowPage } from "../common/config/can_show_page"; import { componentsWithService } from "../common/config/components_with_service"; -import { fireEvent } from "../common/dom/fire_event"; import { computeDomain } from "../common/entity/compute_domain"; import { computeStateDisplay } from "../common/entity/compute_state_display"; import { computeStateName } from "../common/entity/compute_state_name"; import { domainIcon } from "../common/entity/domain_icon"; -import { navigate } from "../common/navigate"; import { caseInsensitiveStringCompare } from "../common/string/compare"; import { ScorableTextItem } from "../common/string/filter/sequence-matching"; -import { - ConfirmationDialogParams, - showConfirmationDialog, -} from "../dialogs/generic/show-dialog-box"; import { QuickBar } from "../dialogs/quick-bar/ha-quick-bar"; import { PageNavigation } from "../layouts/hass-tabs-subpage"; import { configSections } from "../panels/config/ha-panel-config"; import { HomeAssistant } from "../types"; +import { AreaRegistryEntry } from "./area_registry"; +import { computeDeviceName, DeviceRegistryEntry } from "./device_registry"; +import { EntityRegistryEntry } from "./entity_registry"; import { domainToName } from "./integration"; import { getPanelNameTranslationKey } from "./panel"; export interface QuickBarItem extends ScorableTextItem { primaryText: string; primaryTextAlt?: string; - secondaryText: string; + secondaryText?: string; + metaText?: string; categoryKey: | "reload" | "navigation" | "server_control" | "entity" | "suggestion"; - action(data?: any): void; + actionData: string | string[]; iconPath?: string; icon?: string; path?: string; + isSuggestion?: boolean; } export type NavigationInfo = PageNavigation & @@ -47,23 +46,34 @@ export type NavigationInfo = PageNavigation & export type BaseNavigationCommand = Pick; export const generateEntityItems = ( - element: QuickBar, - hass: HomeAssistant + hass: HomeAssistant, + entities: { [entityId: string]: EntityRegistryEntry }, + devices: { [deviceId: string]: DeviceRegistryEntry }, + areas: { [areaId: string]: AreaRegistryEntry } ): QuickBarItem[] => Object.keys(hass.states) .map((entityId) => { const entityState = hass.states[entityId]; + const entity = entities[entityId]; + const deviceName = entity?.device_id + ? computeDeviceName(devices[entity.device_id], hass) + : undefined; const entityItem = { primaryText: computeStateName(entityState), - primaryTextAlt: entityId, - secondaryText: hass.userData?.showAdvanced - ? entityId - : computeStateDisplay(hass.localize, entityState, hass.locale), + primaryTextAlt: computeStateDisplay( + hass.localize, + entityState, + hass.locale + ), + secondaryText: + (deviceName ? `${deviceName} | ` : "") + + (hass.userData?.showAdvanced ? entityId : ""), + metaText: entity?.area_id ? areas[entity.area_id].name : undefined, icon: entityState.attributes.icon, iconPath: entityState.attributes.icon ? undefined : domainIcon(computeDomain(entityId), entityState), - action: () => fireEvent(element, "hass-more-info", { entityId }), + actionData: entityId, categoryKey: "entity" as const, }; @@ -95,21 +105,21 @@ export const generateReloadCommands = (hass: HomeAssistant): QuickBarItem[] => { "domain", domainToName(hass.localize, domain) ), - action: () => hass.callService(domain, "reload"), + actionData: [domain, "reload"], secondaryText: "Reload changes made to the domain file", })); // Add "frontend.reload_themes" commands.push({ primaryText: hass.localize("ui.dialogs.quick-bar.commands.reload.themes"), - action: () => hass.callService("frontend", "reload_themes"), + actionData: ["frontend", "reload_themes"], secondaryText: "Reload changes made to themes.yaml", }); // Add "homeassistant.reload_core_config" commands.push({ primaryText: hass.localize("ui.dialogs.quick-bar.commands.reload.core"), - action: () => hass.callService("homeassistant", "reload_core_config"), + actionData: ["homeassistant", "reload_core_config"], secondaryText: "Reload changes made to configuration.yaml", }); @@ -141,23 +151,19 @@ export const generateServerControlCommands = ( hass.localize(`ui.dialogs.quick-bar.commands.server_control.${action}`) ), categoryKey, - action: () => hass.callService("homeassistant", action), + actionData: action, }; - return generateConfirmationCommand( - element, - { - ...item, - strings: [ - `${hass.localize( - `ui.dialogs.quick-bar.commands.types.${categoryKey}` - )} ${item.primaryText}`, - ], - secondaryText: "Control your server", - iconPath: mdiServerNetwork, - }, - hass.localize("ui.dialogs.generic.ok") - ); + return { + ...item, + strings: [ + `${hass.localize( + `ui.dialogs.quick-bar.commands.types.${categoryKey}` + )} ${item.primaryText}`, + ], + secondaryText: "Control your server", + iconPath: mdiServerNetwork, + }; }); }; @@ -249,19 +255,6 @@ export const getNavigationInfoFromConfig = ( return undefined; }; -export const generateConfirmationCommand = ( - element: QuickBar, - item: QuickBarItem, - confirmText: ConfirmationDialogParams["confirmText"] -): QuickBarItem => ({ - ...item, - action: () => - showConfirmationDialog(element, { - confirmText, - confirm: item.action, - }), -}); - const finalizeNavigationCommands = ( items: BaseNavigationCommand[], hass: HomeAssistant @@ -273,7 +266,7 @@ const finalizeNavigationCommands = ( secondaryText: "Navigation", iconPath: mdiEarth, ...item, - action: () => navigate(item.path!), + actionData: item.path!, }; return { diff --git a/src/dialogs/quick-bar/ha-quick-bar.ts b/src/dialogs/quick-bar/ha-quick-bar.ts index 716a330ad2..cb9899444c 100644 --- a/src/dialogs/quick-bar/ha-quick-bar.ts +++ b/src/dialogs/quick-bar/ha-quick-bar.ts @@ -3,33 +3,50 @@ import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list-item"; import type { ListItem } from "@material/mwc-list/mwc-list-item"; import { mdiClose, mdiMagnify } from "@mdi/js"; -import { css, html, LitElement, TemplateResult } from "lit"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; +import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { ifDefined } from "lit/directives/if-defined"; import { styleMap } from "lit/directives/style-map"; import memoizeOne from "memoize-one"; import { LocalStorage } from "../../common/decorators/local-storage"; import { fireEvent } from "../../common/dom/fire_event"; +import { navigate } from "../../common/navigate"; import { fuzzyFilterSort } from "../../common/string/filter/sequence-matching"; import { debounce } from "../../common/util/debounce"; +import "../../components/ha-chip"; import "../../components/ha-circular-progress"; import "../../components/ha-icon-button"; import "../../components/ha-textfield"; +import { + AreaRegistryEntry, + subscribeAreaRegistry, +} from "../../data/area_registry"; +import { + DeviceRegistryEntry, + subscribeDeviceRegistry, +} from "../../data/device_registry"; +import { + EntityRegistryEntry, + subscribeEntityRegistry, +} from "../../data/entity_registry"; import { generateCommandItems, generateEntityItems, QuickBarItem, } from "../../data/quick-bar"; +import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { haStyle, haStyleDialog, haStyleScrollbar, } from "../../resources/styles"; import type { HomeAssistant } from "../../types"; +import { showConfirmationDialog } from "../generic/show-dialog-box"; import { QuickBarParams } from "./show-dialog-quick-bar"; @customElement("ha-quick-bar") -export class QuickBar extends LitElement { +export class QuickBar extends SubscribeMixin(LitElement) { @property({ attribute: false }) public hass!: HomeAssistant; @state() private _items?: Array; @@ -40,10 +57,6 @@ export class QuickBar extends LitElement { @state() private _search = ""; - @state() private _open = false; - - @state() private _commandMode = false; - @state() private _opened = false; @state() private _done = false; @@ -52,6 +65,12 @@ export class QuickBar extends LitElement { @state() private _hint?: string; + @state() private _entities?: EntityRegistryEntry[]; + + @state() private _areas?: AreaRegistryEntry[]; + + @state() private _devices?: DeviceRegistryEntry[]; + @query("ha-textfield", false) private _filterInputField?: HTMLElement; // @ts-ignore @@ -74,12 +93,23 @@ export class QuickBar extends LitElement { this._narrow = matchMedia( "all and (max-width: 450px), all and (max-height: 500px)" ).matches; - this._initializeItems(); - this._opened = true; + } + + public hassSubscribe(): UnsubscribeFunc[] { + return [ + subscribeAreaRegistry(this.hass.connection!, (areas) => { + this._areas = areas; + }), + subscribeDeviceRegistry(this.hass.connection!, (devices) => { + this._devices = devices; + }), + subscribeEntityRegistry(this.hass.connection!, (entities) => { + this._entities = entities; + }), + ]; } public closeDialog() { - this._open = false; this._opened = false; this._focusSet = false; this._filter = ""; @@ -87,8 +117,23 @@ export class QuickBar extends LitElement { fireEvent(this, "dialog-closed", { dialog: this.localName }); } - protected render() { - if (!this._open) { + protected willUpdate(changedProperties: PropertyValues): void { + super.willUpdate(changedProperties); + if ( + (changedProperties.has("_entity") || + changedProperties.has("_areas") || + changedProperties.has("_devices")) && + this._areas && + this._devices && + this._entities + ) { + this._initializeItems(); + this._opened = true; + } + } + + protected render(): TemplateResult { + if (!this._opened) { return html``; } @@ -176,28 +221,6 @@ export class QuickBar extends LitElement { ` : html` - <<<<<<< HEAD - ${this._opened - ? html` - ` - : ""} - ======= - >>>>>>> 64654972a (Stash) `} ${this._hint ? html`
${this._hint}
` : ""} @@ -238,14 +260,19 @@ export class QuickBar extends LitElement { ${index === 0 || item?.categoryKey !== previous?.categoryKey ? html`
- ${this.hass.localize( - `ui.dialogs.quick-bar.commands.types.${item.categoryKey}` - )} + ${item.isSuggestion + ? this.hass.localize( + "ui.dialogs.quick-bar.commands.types.suggestions" + ) + : this.hass.localize( + `ui.dialogs.quick-bar.commands.types.${item.categoryKey}` + )}
` : ""} ` : ""} + ${item.metaText + ? html`${item.metaText}` + : ""} `; }; private _initializeItems() { + const deviceLookup: { [deviceId: string]: DeviceRegistryEntry } = {}; + for (const device of this._devices!) { + deviceLookup[device.id] = device; + } + + const entityLookup: { [entityId: string]: EntityRegistryEntry } = {}; + for (const entity of this._entities!) { + entityLookup[entity.entity_id] = entity; + } + + const areaLookup: { [areaId: string]: AreaRegistryEntry } = {}; + for (const area of this._areas!) { + areaLookup[area.area_id] = area; + } + this._items = this._items || [ - generateEntityItems(this, this.hass), + generateEntityItems(this.hass, entityLookup, deviceLookup, areaLookup), ...generateCommandItems(this, this.hass), ]; } private async processItemAndCloseDialog(item: QuickBarItem, index: number) { if (!this._suggestions.includes(item)) { - this._suggestions.unshift({ ...item, categoryKey: "suggestion" }); + this._suggestions.unshift({ ...item, isSuggestion: true }); this._suggestions = this._suggestions.slice(0, 3); } this._addSpinnerToItem(index); - await item.action(); + switch (item.categoryKey) { + case "entity": + fireEvent(this, "hass-more-info", { + entityId: item.actionData as string, + }); + break; + case "reload": + this.hass.callService(item.actionData[0], item.actionData[1]); + break; + case "navigation": + navigate(item.actionData as string); + break; + case "server_control": + showConfirmationDialog(this, { + confirmText: this.hass.localize("ui.dialogs.generic.ok"), + confirm: () => + this.hass.callService("homeassistant", item.actionData as string), + }); + break; + } this.closeDialog(); } @@ -416,17 +480,6 @@ export class QuickBar extends LitElement { this._getItemAtIndex(index)?.appendChild(spinner); } - private _getSuggestionsWithActions(): QuickBarItem[] { - return this._suggestions.map((item) => { - let action; - switch (item.categoryKey) { - case "entity": - action = () => fireEvent(this, "hass-more-info", {}); - } - return { ...item, action }; - }); - } - static get styles() { return [ haStyleScrollbar, @@ -523,6 +576,10 @@ export class QuickBar extends LitElement { lit-virtualizer { contain: size layout !important; } + + ha-chip { + margin-left: -48px; + } `, ]; }