mirror of
https://github.com/home-assistant/frontend.git
synced 2026-01-11 01:37:28 +00:00
Compare commits
11 Commits
clock-date
...
quick-bar-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
126e0b76c0 | ||
|
|
167dffda8f | ||
|
|
bc057b5187 | ||
|
|
dc0d8932ad | ||
|
|
84472483ab | ||
|
|
8d9ab20c73 | ||
|
|
8c0d8d5b3b | ||
|
|
92156663ab | ||
|
|
7d67ea5c61 | ||
|
|
d6bcb143cc | ||
|
|
41054400bf |
@@ -1,4 +1,4 @@
|
||||
import type { AreaRegistryEntry } from "../../../src/data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../src/data/area/area_registry";
|
||||
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockAreaRegistry = (
|
||||
|
||||
@@ -10,7 +10,7 @@ import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervis
|
||||
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
|
||||
import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
|
||||
@@ -11,7 +11,7 @@ import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
import "../../../../src/components/ha-selector/ha-selector";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area/area_registry";
|
||||
import type { BlueprintInput } from "../../../../src/data/blueprint";
|
||||
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
|
||||
import type { FloorRegistryEntry } from "../../../../src/data/floor_registry";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AreaRegistryEntry } from "../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../data/area/area_registry";
|
||||
import type { FloorRegistryEntry } from "../../data/floor_registry";
|
||||
|
||||
export interface AreasFloorHierarchy {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AreaRegistryEntry } from "../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../data/area/area_registry";
|
||||
|
||||
export const computeAreaName = (area: AreaRegistryEntry): string | undefined =>
|
||||
area.name?.trim();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type { FloorRegistryEntry } from "../../../data/floor_registry";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
|
||||
import type { FloorRegistryEntry } from "../../../data/floor_registry";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
|
||||
import type {
|
||||
EntityRegistryDisplayEntry,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { listenMediaQuery } from "../common/dom/media_query";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-bottom-sheet";
|
||||
import "./ha-dialog-header";
|
||||
import "./ha-icon-button";
|
||||
@@ -118,27 +118,29 @@ export class HaAdaptiveDialog extends LitElement {
|
||||
if (this._mode === "bottom-sheet") {
|
||||
return html`
|
||||
<ha-bottom-sheet .open=${this.open} flexcontent>
|
||||
<ha-dialog-header
|
||||
slot="header"
|
||||
.subtitlePosition=${this.headerSubtitlePosition}
|
||||
>
|
||||
<slot name="headerNavigationIcon" slot="navigationIcon">
|
||||
<ha-icon-button
|
||||
data-drawer="close"
|
||||
.label=${this.hass?.localize("ui.common.close") ?? "Close"}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
</slot>
|
||||
${this.headerTitle !== undefined
|
||||
? html`<span slot="title" class="title" id="ha-wa-dialog-title">
|
||||
${this.headerTitle}
|
||||
</span>`
|
||||
: html`<slot name="headerTitle" slot="title"></slot>`}
|
||||
${this.headerSubtitle !== undefined
|
||||
? html`<span slot="subtitle">${this.headerSubtitle}</span>`
|
||||
: html`<slot name="headerSubtitle" slot="subtitle"></slot>`}
|
||||
<slot name="headerActionItems" slot="actionItems"></slot>
|
||||
</ha-dialog-header>
|
||||
<slot name="header" slot="header">
|
||||
<ha-dialog-header
|
||||
slot="header"
|
||||
.subtitlePosition=${this.headerSubtitlePosition}
|
||||
>
|
||||
<slot name="headerNavigationIcon" slot="navigationIcon">
|
||||
<ha-icon-button
|
||||
data-drawer="close"
|
||||
.label=${this.hass?.localize("ui.common.close") ?? "Close"}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
</slot>
|
||||
${this.headerTitle !== undefined
|
||||
? html`<span slot="title" class="title" id="ha-wa-dialog-title">
|
||||
${this.headerTitle}
|
||||
</span>`
|
||||
: html`<slot name="headerTitle" slot="title"></slot>`}
|
||||
${this.headerSubtitle !== undefined
|
||||
? html`<span slot="subtitle">${this.headerSubtitle}</span>`
|
||||
: html`<slot name="headerSubtitle" slot="subtitle"></slot>`}
|
||||
<slot name="headerActionItems" slot="actionItems"></slot>
|
||||
</ha-dialog-header>
|
||||
</slot>
|
||||
<slot></slot>
|
||||
<slot name="footer" slot="footer"></slot>
|
||||
</ha-bottom-sheet>
|
||||
@@ -157,16 +159,18 @@ export class HaAdaptiveDialog extends LitElement {
|
||||
.headerSubtitlePosition=${this.headerSubtitlePosition}
|
||||
flexcontent
|
||||
>
|
||||
<slot name="headerNavigationIcon" slot="headerNavigationIcon">
|
||||
<ha-icon-button
|
||||
data-dialog="close"
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<slot name="header" slot="header">
|
||||
<slot name="headerNavigationIcon" slot="headerNavigationIcon">
|
||||
<ha-icon-button
|
||||
data-dialog="close"
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
</slot>
|
||||
<slot name="headerTitle" slot="headerTitle"></slot>
|
||||
<slot name="headerSubtitle" slot="headerSubtitle"></slot>
|
||||
<slot name="headerActionItems" slot="headerActionItems"></slot>
|
||||
</slot>
|
||||
<slot name="headerTitle" slot="headerTitle"></slot>
|
||||
<slot name="headerSubtitle" slot="headerSubtitle"></slot>
|
||||
<slot name="headerActionItems" slot="headerActionItems"></slot>
|
||||
<slot></slot>
|
||||
<slot name="footer" slot="footer"></slot>
|
||||
</ha-wa-dialog>
|
||||
|
||||
@@ -6,16 +6,10 @@ import { customElement, property, query } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeAreaName } from "../common/entity/compute_area_name";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { computeFloorName } from "../common/entity/compute_floor_name";
|
||||
import { getAreaContext } from "../common/entity/context/get_area_context";
|
||||
import { createAreaRegistryEntry } from "../data/area_registry";
|
||||
import type {
|
||||
DeviceEntityDisplayLookup,
|
||||
DeviceRegistryEntry,
|
||||
} from "../data/device/device_registry";
|
||||
import { getDeviceEntityDisplayLookup } from "../data/device/device_registry";
|
||||
import type { EntityRegistryDisplayEntry } from "../data/entity/entity_registry";
|
||||
import { areaComboBoxKeys, getAreas } from "../data/area/area_picker";
|
||||
import { createAreaRegistryEntry } from "../data/area/area_registry";
|
||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||
import { showAreaRegistryDetailDialog } from "../panels/config/areas/show-dialog-area-registry-detail";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
@@ -30,12 +24,6 @@ import "./ha-svg-icon";
|
||||
|
||||
const ADD_NEW_ID = "___ADD_NEW___";
|
||||
|
||||
const SEARCH_KEYS = [
|
||||
{ name: "search_labels.areaName", weight: 10 },
|
||||
{ name: "search_labels.aliases", weight: 8 },
|
||||
{ name: "search_labels.floorName", weight: 6 },
|
||||
{ name: "search_labels.id", weight: 3 },
|
||||
];
|
||||
@customElement("ha-area-picker")
|
||||
export class HaAreaPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -102,6 +90,8 @@ export class HaAreaPicker extends LitElement {
|
||||
await this._picker?.open();
|
||||
}
|
||||
|
||||
private _getAreasMemoized = memoizeOne(getAreas);
|
||||
|
||||
// Recompute value renderer when the areas change
|
||||
private _computeValueRenderer = memoizeOne(
|
||||
(_haAreas: HomeAssistant["areas"]): PickerValueRenderer =>
|
||||
@@ -137,183 +127,13 @@ export class HaAreaPicker extends LitElement {
|
||||
}
|
||||
);
|
||||
|
||||
private _getAreas = memoizeOne(
|
||||
(
|
||||
haAreas: HomeAssistant["areas"],
|
||||
haDevices: HomeAssistant["devices"],
|
||||
haEntities: HomeAssistant["entities"],
|
||||
includeDomains: this["includeDomains"],
|
||||
excludeDomains: this["excludeDomains"],
|
||||
includeDeviceClasses: this["includeDeviceClasses"],
|
||||
deviceFilter: this["deviceFilter"],
|
||||
entityFilter: this["entityFilter"],
|
||||
excludeAreas: this["excludeAreas"]
|
||||
): PickerComboBoxItem[] => {
|
||||
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
|
||||
let inputDevices: DeviceRegistryEntry[] | undefined;
|
||||
let inputEntities: EntityRegistryDisplayEntry[] | undefined;
|
||||
|
||||
const areas = Object.values(haAreas);
|
||||
const devices = Object.values(haDevices);
|
||||
const entities = Object.values(haEntities);
|
||||
|
||||
if (
|
||||
includeDomains ||
|
||||
excludeDomains ||
|
||||
includeDeviceClasses ||
|
||||
deviceFilter ||
|
||||
entityFilter
|
||||
) {
|
||||
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
|
||||
inputDevices = devices;
|
||||
inputEntities = entities.filter((entity) => entity.area_id);
|
||||
|
||||
if (includeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return true;
|
||||
}
|
||||
return entities.every(
|
||||
(entity) =>
|
||||
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter(
|
||||
(entity) =>
|
||||
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (includeDeviceClasses) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (deviceFilter) {
|
||||
inputDevices = inputDevices!.filter((device) =>
|
||||
deviceFilter!(device)
|
||||
);
|
||||
}
|
||||
|
||||
if (entityFilter) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter(stateObj);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter!(stateObj);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let outputAreas = areas;
|
||||
|
||||
let areaIds: string[] | undefined;
|
||||
|
||||
if (inputDevices) {
|
||||
areaIds = inputDevices
|
||||
.filter((device) => device.area_id)
|
||||
.map((device) => device.area_id!);
|
||||
}
|
||||
|
||||
if (inputEntities) {
|
||||
areaIds = (areaIds ?? []).concat(
|
||||
inputEntities
|
||||
.filter((entity) => entity.area_id)
|
||||
.map((entity) => entity.area_id!)
|
||||
);
|
||||
}
|
||||
|
||||
if (areaIds) {
|
||||
outputAreas = outputAreas.filter((area) =>
|
||||
areaIds!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeAreas) {
|
||||
outputAreas = outputAreas.filter(
|
||||
(area) => !excludeAreas!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
const items = outputAreas.map<PickerComboBoxItem>((area) => {
|
||||
const { floor } = getAreaContext(area, this.hass.floors);
|
||||
const floorName = floor ? computeFloorName(floor) : undefined;
|
||||
const areaName = computeAreaName(area);
|
||||
return {
|
||||
id: area.area_id,
|
||||
primary: areaName || area.area_id,
|
||||
secondary: floorName,
|
||||
icon: area.icon || undefined,
|
||||
icon_path: area.icon ? undefined : mdiTextureBox,
|
||||
search_labels: {
|
||||
areaName: areaName || null,
|
||||
floorName: floorName || null,
|
||||
id: area.area_id,
|
||||
aliases: area.aliases.join(" "),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
);
|
||||
|
||||
private _getItems = () =>
|
||||
this._getAreas(
|
||||
this._getAreasMemoized(
|
||||
this.hass.areas,
|
||||
this.hass.floors,
|
||||
this.hass.devices,
|
||||
this.hass.entities,
|
||||
this.hass.states,
|
||||
this.includeDomains,
|
||||
this.excludeDomains,
|
||||
this.includeDeviceClasses,
|
||||
@@ -394,7 +214,7 @@ export class HaAreaPicker extends LitElement {
|
||||
.getAdditionalItems=${this._getAdditionalItems}
|
||||
.valueRenderer=${valueRenderer}
|
||||
.addButtonLabel=${this.addButtonLabel}
|
||||
.searchKeys=${SEARCH_KEYS}
|
||||
.searchKeys=${areaComboBoxKeys}
|
||||
.unknownItemText=${this.hass.localize(
|
||||
"ui.components.area-picker.unknown"
|
||||
)}
|
||||
|
||||
@@ -8,7 +8,7 @@ import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { computeFloorName } from "../common/entity/compute_floor_name";
|
||||
import { updateAreaRegistryEntry } from "../data/area_registry";
|
||||
import { updateAreaRegistryEntry } from "../data/area/area_registry";
|
||||
import type {
|
||||
DeviceEntityDisplayLookup,
|
||||
DeviceRegistryEntry,
|
||||
|
||||
@@ -146,14 +146,14 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
@property({ attribute: "selected-section" }) public selectedSection?: string;
|
||||
|
||||
@query("lit-virtualizer") private _virtualizerElement?: LitVirtualizer;
|
||||
@query("lit-virtualizer") public virtualizerElement?: LitVirtualizer;
|
||||
|
||||
@query("ha-textfield") private _searchFieldElement?: HaTextField;
|
||||
|
||||
@state() private _items: (PickerComboBoxItem | string)[] = [];
|
||||
|
||||
protected get scrollableElement(): HTMLElement | null {
|
||||
return this._virtualizerElement as HTMLElement | null;
|
||||
return this.virtualizerElement as HTMLElement | null;
|
||||
}
|
||||
|
||||
@state() private _sectionTitle?: string;
|
||||
@@ -200,6 +200,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
return html`<ha-textfield
|
||||
.label=${searchLabel}
|
||||
@blur=${this._resetSelectedItem}
|
||||
@input=${this._filterChanged}
|
||||
></ha-textfield>
|
||||
${this._renderSectionButtons()}
|
||||
@@ -237,6 +238,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
@unpinned=${this._handleUnpinned}
|
||||
@scroll=${this._onScrollList}
|
||||
@focus=${this._focusList}
|
||||
@blur=${this._resetSelectedItem}
|
||||
@visibilityChanged=${this._visibilityChanged}
|
||||
>
|
||||
</lit-virtualizer>
|
||||
@@ -269,18 +271,18 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
@eventOptions({ passive: true })
|
||||
private _visibilityChanged(ev) {
|
||||
if (
|
||||
this._virtualizerElement &&
|
||||
this.virtualizerElement &&
|
||||
this.sectionTitleFunction &&
|
||||
this.sections?.length
|
||||
) {
|
||||
const firstItem = this._virtualizerElement.items[ev.first];
|
||||
const secondItem = this._virtualizerElement.items[ev.first + 1];
|
||||
const firstItem = this.virtualizerElement.items[ev.first];
|
||||
const secondItem = this.virtualizerElement.items[ev.first + 1];
|
||||
this._sectionTitle = this.sectionTitleFunction({
|
||||
firstIndex: ev.first,
|
||||
lastIndex: ev.last,
|
||||
firstItem: firstItem as PickerComboBoxItem | string,
|
||||
secondItem: secondItem as PickerComboBoxItem | string,
|
||||
itemsCount: this._virtualizerElement.items.length,
|
||||
itemsCount: this.virtualizerElement.items.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -396,11 +398,17 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
private _valueSelected = (ev: Event) => {
|
||||
ev.stopPropagation();
|
||||
const value = (ev.currentTarget as any).value as string;
|
||||
const index = Number((ev.currentTarget as any).index);
|
||||
const newValue = value?.trim();
|
||||
|
||||
fireEvent(this, "value-changed", { value: newValue });
|
||||
this._fireSelectedEvents(newValue, index);
|
||||
};
|
||||
|
||||
private _fireSelectedEvents(value: string, index: number) {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
fireEvent(this, "index-selected", { index });
|
||||
}
|
||||
|
||||
private _fuseIndex = memoizeOne(
|
||||
(states: PickerComboBoxItem[], searchKeys?: FuseWeightedKey[]) =>
|
||||
Fuse.createIndex(searchKeys || DEFAULT_SEARCH_KEYS, states)
|
||||
@@ -483,8 +491,8 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
this._items = this._getItems();
|
||||
|
||||
// Reset scroll position when filter changes
|
||||
if (this._virtualizerElement) {
|
||||
this._virtualizerElement.scrollToIndex(0);
|
||||
if (this.virtualizerElement) {
|
||||
this.virtualizerElement.scrollToIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,13 +515,13 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
private _selectNextItem = (ev?: KeyboardEvent) => {
|
||||
ev?.stopPropagation();
|
||||
ev?.preventDefault();
|
||||
if (!this._virtualizerElement) {
|
||||
if (!this.virtualizerElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._searchFieldElement?.focus();
|
||||
|
||||
const items = this._virtualizerElement.items as PickerComboBoxItem[];
|
||||
const items = this.virtualizerElement.items as PickerComboBoxItem[];
|
||||
|
||||
const maxItems = items.length - 1;
|
||||
|
||||
@@ -547,14 +555,14 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
private _selectPreviousItem = (ev: KeyboardEvent) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
if (!this._virtualizerElement) {
|
||||
if (!this.virtualizerElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._selectedItemIndex > 0) {
|
||||
const nextIndex = this._selectedItemIndex - 1;
|
||||
|
||||
const items = this._virtualizerElement.items as PickerComboBoxItem[];
|
||||
const items = this.virtualizerElement.items as PickerComboBoxItem[];
|
||||
|
||||
if (!items[nextIndex]) {
|
||||
return;
|
||||
@@ -576,13 +584,13 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
private _selectFirstItem = (ev: KeyboardEvent) => {
|
||||
ev.stopPropagation();
|
||||
if (!this._virtualizerElement || !this._virtualizerElement.items.length) {
|
||||
if (!this.virtualizerElement || !this.virtualizerElement.items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextIndex = 0;
|
||||
|
||||
if (typeof this._virtualizerElement.items[nextIndex] === "string") {
|
||||
if (typeof this.virtualizerElement.items[nextIndex] === "string") {
|
||||
this._selectedItemIndex = nextIndex + 1;
|
||||
} else {
|
||||
this._selectedItemIndex = nextIndex;
|
||||
@@ -593,13 +601,13 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
private _selectLastItem = (ev: KeyboardEvent) => {
|
||||
ev.stopPropagation();
|
||||
if (!this._virtualizerElement || !this._virtualizerElement.items.length) {
|
||||
if (!this.virtualizerElement || !this.virtualizerElement.items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextIndex = this._virtualizerElement.items.length - 1;
|
||||
const nextIndex = this.virtualizerElement.items.length - 1;
|
||||
|
||||
if (typeof this._virtualizerElement.items[nextIndex] === "string") {
|
||||
if (typeof this.virtualizerElement.items[nextIndex] === "string") {
|
||||
this._selectedItemIndex = nextIndex - 1;
|
||||
} else {
|
||||
this._selectedItemIndex = nextIndex;
|
||||
@@ -609,14 +617,14 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
};
|
||||
|
||||
private _scrollToSelectedItem = () => {
|
||||
this._virtualizerElement
|
||||
this.virtualizerElement
|
||||
?.querySelector(".selected")
|
||||
?.classList.remove("selected");
|
||||
|
||||
this._virtualizerElement?.scrollToIndex(this._selectedItemIndex, "end");
|
||||
this.virtualizerElement?.scrollToIndex(this._selectedItemIndex, "end");
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
this._virtualizerElement
|
||||
this.virtualizerElement
|
||||
?.querySelector(`#list-item-${this._selectedItemIndex}`)
|
||||
?.classList.add("selected");
|
||||
});
|
||||
@@ -624,12 +632,20 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
private _pickSelectedItem = (ev: KeyboardEvent) => {
|
||||
ev.stopPropagation();
|
||||
const firstItem = this._virtualizerElement?.items[0] as PickerComboBoxItem;
|
||||
|
||||
if (this._virtualizerElement?.items.length === 1) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: firstItem.id,
|
||||
if (
|
||||
this.virtualizerElement?.items?.length !== undefined &&
|
||||
this.virtualizerElement.items.length < 4 && // it still can have a section title and a padding item
|
||||
this.virtualizerElement.items.filter((item) => typeof item !== "string")
|
||||
.length === 1
|
||||
) {
|
||||
(
|
||||
this.virtualizerElement?.items as (PickerComboBoxItem | string)[]
|
||||
).forEach((item, index) => {
|
||||
if (typeof item !== "string") {
|
||||
this._fireSelectedEvents(item.id, index);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._selectedItemIndex === -1) {
|
||||
@@ -639,16 +655,16 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
// if filter button is focused
|
||||
ev.preventDefault();
|
||||
|
||||
const item = this._virtualizerElement?.items[
|
||||
const item = this.virtualizerElement?.items[
|
||||
this._selectedItemIndex
|
||||
] as PickerComboBoxItem;
|
||||
if (item) {
|
||||
fireEvent(this, "value-changed", { value: item.id });
|
||||
this._fireSelectedEvents(item.id, this._selectedItemIndex);
|
||||
}
|
||||
};
|
||||
|
||||
private _resetSelectedItem() {
|
||||
this._virtualizerElement
|
||||
this.virtualizerElement
|
||||
?.querySelector(".selected")
|
||||
?.classList.remove("selected");
|
||||
this._selectedItemIndex = -1;
|
||||
@@ -658,11 +674,11 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
typeof item === "string" ? item : item?.id;
|
||||
|
||||
private _getInitialSelectedIndex() {
|
||||
if (!this._virtualizerElement || this._search || !this.value) {
|
||||
if (!this.virtualizerElement || this._search || !this.value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const index = this._virtualizerElement.items.findIndex(
|
||||
const index = this.virtualizerElement.items.findIndex(
|
||||
(item) =>
|
||||
typeof item !== "string" &&
|
||||
(item as PickerComboBoxItem).id === this.value
|
||||
@@ -842,4 +858,8 @@ declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-picker-combo-box": HaPickerComboBox;
|
||||
}
|
||||
|
||||
interface HASSDomEvents {
|
||||
"index-selected": { index: number };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { computeEntityName } from "../../common/entity/compute_entity_name";
|
||||
import { getEntityContext } from "../../common/entity/context/get_entity_context";
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import type { AreaRegistryEntry } from "../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../data/area/area_registry";
|
||||
import { getConfigEntry } from "../../data/config_entries";
|
||||
import { labelsContext } from "../../data/context";
|
||||
import type { DeviceRegistryEntry } from "../../data/device/device_registry";
|
||||
|
||||
199
src/data/area/area_picker.ts
Normal file
199
src/data/area/area_picker.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import { mdiTextureBox } from "@mdi/js";
|
||||
import { computeAreaName } from "../../common/entity/compute_area_name";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { computeFloorName } from "../../common/entity/compute_floor_name";
|
||||
import { getAreaContext } from "../../common/entity/context/get_area_context";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "../../components/device/ha-device-picker";
|
||||
import type { PickerComboBoxItem } from "../../components/ha-picker-combo-box";
|
||||
import type { FuseWeightedKey } from "../../resources/fuseMultiTerm";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import {
|
||||
getDeviceEntityDisplayLookup,
|
||||
type DeviceEntityDisplayLookup,
|
||||
type DeviceRegistryEntry,
|
||||
} from "../device/device_registry";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "../entity/entity";
|
||||
import type { EntityRegistryDisplayEntry } from "../entity/entity_registry";
|
||||
|
||||
export const getAreas = (
|
||||
haAreas: HomeAssistant["areas"],
|
||||
haFloors: HomeAssistant["floors"],
|
||||
haDevices: HomeAssistant["devices"],
|
||||
haEntities: HomeAssistant["entities"],
|
||||
haStates: HomeAssistant["states"],
|
||||
includeDomains?: string[],
|
||||
excludeDomains?: string[],
|
||||
includeDeviceClasses?: string[],
|
||||
deviceFilter?: HaDevicePickerDeviceFilterFunc,
|
||||
entityFilter?: HaEntityPickerEntityFilterFunc,
|
||||
excludeAreas?: string[],
|
||||
idPrefix = ""
|
||||
): PickerComboBoxItem[] => {
|
||||
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
|
||||
let inputDevices: DeviceRegistryEntry[] | undefined;
|
||||
let inputEntities: EntityRegistryDisplayEntry[] | undefined;
|
||||
|
||||
const areas = Object.values(haAreas);
|
||||
const devices = Object.values(haDevices);
|
||||
const entities = Object.values(haEntities);
|
||||
|
||||
if (
|
||||
includeDomains ||
|
||||
excludeDomains ||
|
||||
includeDeviceClasses ||
|
||||
deviceFilter ||
|
||||
entityFilter
|
||||
) {
|
||||
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
|
||||
inputDevices = devices;
|
||||
inputEntities = entities.filter((entity) => entity.area_id);
|
||||
|
||||
if (includeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return true;
|
||||
}
|
||||
return entities.every(
|
||||
(entity) => !excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter(
|
||||
(entity) => !excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (includeDeviceClasses) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = haStates[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = haStates[entity.entity_id];
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (deviceFilter) {
|
||||
inputDevices = inputDevices!.filter((device) => deviceFilter!(device));
|
||||
}
|
||||
|
||||
if (entityFilter) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = haStates[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter(stateObj);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = haStates[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter!(stateObj);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let outputAreas = areas;
|
||||
|
||||
let areaIds: string[] | undefined;
|
||||
|
||||
if (inputDevices) {
|
||||
areaIds = inputDevices
|
||||
.filter((device) => device.area_id)
|
||||
.map((device) => device.area_id!);
|
||||
}
|
||||
|
||||
if (inputEntities) {
|
||||
areaIds = (areaIds ?? []).concat(
|
||||
inputEntities
|
||||
.filter((entity) => entity.area_id)
|
||||
.map((entity) => entity.area_id!)
|
||||
);
|
||||
}
|
||||
|
||||
if (areaIds) {
|
||||
outputAreas = outputAreas.filter((area) => areaIds!.includes(area.area_id));
|
||||
}
|
||||
|
||||
if (excludeAreas) {
|
||||
outputAreas = outputAreas.filter(
|
||||
(area) => !excludeAreas!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
const items = outputAreas.map<PickerComboBoxItem>((area) => {
|
||||
const { floor } = getAreaContext(area, haFloors);
|
||||
const floorName = floor ? computeFloorName(floor) : undefined;
|
||||
const areaName = computeAreaName(area);
|
||||
return {
|
||||
id: `${idPrefix}${area.area_id}`,
|
||||
primary: areaName || area.area_id,
|
||||
secondary: floorName,
|
||||
icon: area.icon || undefined,
|
||||
icon_path: area.icon ? undefined : mdiTextureBox,
|
||||
search_labels: {
|
||||
aliases: area.aliases.join(" "),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
export const areaComboBoxKeys: FuseWeightedKey[] = [
|
||||
{
|
||||
name: "primary",
|
||||
weight: 10,
|
||||
},
|
||||
{
|
||||
name: "search_labels.aliases",
|
||||
weight: 8,
|
||||
},
|
||||
{
|
||||
name: "secondary",
|
||||
weight: 8,
|
||||
},
|
||||
{
|
||||
name: "search_labels.domain",
|
||||
weight: 4,
|
||||
},
|
||||
];
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { DeviceRegistryEntry } from "./device/device_registry";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { DeviceRegistryEntry } from "../device/device_registry";
|
||||
import type {
|
||||
EntityRegistryDisplayEntry,
|
||||
EntityRegistryEntry,
|
||||
} from "./entity/entity_registry";
|
||||
import type { RegistryEntry } from "./registry";
|
||||
} from "../entity/entity_registry";
|
||||
import type { RegistryEntry } from "../registry";
|
||||
|
||||
export { subscribeAreaRegistry } from "./ws-area_registry";
|
||||
export { subscribeAreaRegistry } from "../ws-area_registry";
|
||||
|
||||
export interface AreaRegistryEntry extends RegistryEntry {
|
||||
aliases: string[];
|
||||
@@ -6,7 +6,7 @@ import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-dev
|
||||
import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
|
||||
import type { FuseWeightedKey } from "../resources/fuseMultiTerm";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { AreaRegistryEntry } from "./area_registry";
|
||||
import type { AreaRegistryEntry } from "./area/area_registry";
|
||||
import {
|
||||
getDeviceEntityDisplayLookup,
|
||||
type DeviceEntityDisplayLookup,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { AreaRegistryEntry } from "./area_registry";
|
||||
import type { AreaRegistryEntry } from "./area/area_registry";
|
||||
import type { RegistryEntry } from "./registry";
|
||||
|
||||
export { subscribeAreaRegistry } from "./ws-area_registry";
|
||||
|
||||
327
src/data/quick_bar.ts
Normal file
327
src/data/quick_bar.ts
Normal file
@@ -0,0 +1,327 @@
|
||||
import {
|
||||
mdiKeyboard,
|
||||
mdiNavigationVariant,
|
||||
mdiPuzzle,
|
||||
mdiReload,
|
||||
mdiServerNetwork,
|
||||
mdiStorePlus,
|
||||
} from "@mdi/js";
|
||||
import { canShowPage } from "../common/config/can_show_page";
|
||||
import { componentsWithService } from "../common/config/components_with_service";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
|
||||
import type { PageNavigation } from "../layouts/hass-tabs-subpage";
|
||||
import { configSections } from "../panels/config/ha-panel-config";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { HassioAddonInfo } from "./hassio/addon";
|
||||
import { domainToName } from "./integration";
|
||||
import { getPanelIcon, getPanelNameTranslationKey } from "./panel";
|
||||
import type { FuseWeightedKey } from "../resources/fuseMultiTerm";
|
||||
|
||||
export interface NavigationComboBoxItem extends PickerComboBoxItem {
|
||||
path: string;
|
||||
image?: string;
|
||||
iconColor?: string;
|
||||
}
|
||||
|
||||
export interface BaseNavigationCommand {
|
||||
path: string;
|
||||
primary: string;
|
||||
icon_path?: string;
|
||||
iconPath?: string;
|
||||
iconColor?: string;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export interface ActionCommandComboBoxItem extends PickerComboBoxItem {
|
||||
action: string;
|
||||
domain?: string;
|
||||
}
|
||||
|
||||
export interface NavigationInfo extends PageNavigation {
|
||||
primary: string;
|
||||
}
|
||||
|
||||
const generateNavigationPanelCommands = (
|
||||
localize: HomeAssistant["localize"],
|
||||
panels: HomeAssistant["panels"],
|
||||
addons?: HassioAddonInfo[]
|
||||
): BaseNavigationCommand[] =>
|
||||
Object.entries(panels)
|
||||
.filter(
|
||||
([panelKey]) => panelKey !== "_my_redirect" && panelKey !== "hassio"
|
||||
)
|
||||
.map(([_panelKey, panel]) => {
|
||||
const translationKey = getPanelNameTranslationKey(panel);
|
||||
const icon = getPanelIcon(panel) || "mdi:view-dashboard";
|
||||
|
||||
const primary = localize(translationKey) || panel.title || panel.url_path;
|
||||
|
||||
let image: string | undefined;
|
||||
|
||||
if (addons) {
|
||||
const addon = addons.find(({ slug }) => slug === panel.url_path);
|
||||
if (addon) {
|
||||
image = addon.icon
|
||||
? `/api/hassio/addons/${addon.slug}/icon`
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
primary,
|
||||
icon,
|
||||
image,
|
||||
path: `/${panel.url_path}`,
|
||||
};
|
||||
});
|
||||
|
||||
const getNavigationInfoFromConfig = (
|
||||
localize: HomeAssistant["localize"],
|
||||
page: PageNavigation
|
||||
): NavigationInfo | undefined => {
|
||||
const path = page.path.substring(1);
|
||||
|
||||
let name = path.substring(path.indexOf("/") + 1);
|
||||
name = name.indexOf("/") > -1 ? name.substring(0, name.indexOf("/")) : name;
|
||||
|
||||
const caption =
|
||||
(name && localize(`ui.dialogs.quick-bar.commands.navigation.${name}`)) ||
|
||||
// @ts-expect-error
|
||||
(page.translationKey && localize(page.translationKey));
|
||||
|
||||
if (caption) {
|
||||
return { ...page, primary: caption };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const generateNavigationConfigSectionCommands = (
|
||||
hass: HomeAssistant
|
||||
): BaseNavigationCommand[] => {
|
||||
if (!hass.user?.is_admin) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const items: NavigationInfo[] = [];
|
||||
|
||||
Object.values(configSections).forEach((sectionPages) => {
|
||||
sectionPages.forEach((page) => {
|
||||
if (!canShowPage(hass, page)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const info = getNavigationInfoFromConfig(hass.localize, page);
|
||||
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
// Add to list, but only if we do not already have an entry for the same path and component
|
||||
if (items.some((e) => e.path === info.path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.push(info);
|
||||
});
|
||||
});
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
const finalizeNavigationCommands = (
|
||||
localize: HomeAssistant["localize"],
|
||||
items: BaseNavigationCommand[]
|
||||
): NavigationComboBoxItem[] =>
|
||||
items.map((item, index) => {
|
||||
const secondary = localize(
|
||||
"ui.dialogs.quick-bar.commands.types.navigation"
|
||||
);
|
||||
return {
|
||||
id: `navigation_${index}_${item.path}`,
|
||||
icon_path: item.iconPath || mdiNavigationVariant,
|
||||
secondary,
|
||||
sorting_label: `${item.primary}_${secondary}`,
|
||||
...item,
|
||||
};
|
||||
});
|
||||
|
||||
export const generateNavigationCommands = (
|
||||
hass: HomeAssistant,
|
||||
addons?: HassioAddonInfo[]
|
||||
): NavigationComboBoxItem[] => {
|
||||
const panelItems = generateNavigationPanelCommands(
|
||||
hass.localize,
|
||||
hass.panels,
|
||||
addons
|
||||
);
|
||||
const sectionItems = generateNavigationConfigSectionCommands(hass);
|
||||
const supervisorItems: BaseNavigationCommand[] = [];
|
||||
if (hass.user?.is_admin && isComponentLoaded(hass, "hassio")) {
|
||||
supervisorItems.push({
|
||||
path: "/hassio/store",
|
||||
icon_path: mdiStorePlus,
|
||||
primary: hass.localize(
|
||||
"ui.dialogs.quick-bar.commands.navigation.addon_store"
|
||||
),
|
||||
});
|
||||
supervisorItems.push({
|
||||
path: "/hassio/dashboard",
|
||||
icon_path: mdiPuzzle,
|
||||
primary: hass.localize(
|
||||
"ui.dialogs.quick-bar.commands.navigation.addon_dashboard"
|
||||
),
|
||||
});
|
||||
if (addons) {
|
||||
for (const addon of addons.filter((a) => a.version)) {
|
||||
supervisorItems.push({
|
||||
path: `/hassio/addon/${addon.slug}`,
|
||||
image: addon.icon
|
||||
? `/api/hassio/addons/${addon.slug}/icon`
|
||||
: undefined,
|
||||
primary: hass.localize(
|
||||
"ui.dialogs.quick-bar.commands.navigation.addon_info",
|
||||
{ addon: addon.name }
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const additionalItems = [
|
||||
{
|
||||
path: "",
|
||||
primary: hass.localize(
|
||||
"ui.dialogs.quick-bar.commands.navigation.shortcuts"
|
||||
),
|
||||
icon_path: mdiKeyboard,
|
||||
},
|
||||
];
|
||||
|
||||
return finalizeNavigationCommands(hass.localize, [
|
||||
...panelItems,
|
||||
...sectionItems,
|
||||
...supervisorItems,
|
||||
...additionalItems,
|
||||
]);
|
||||
};
|
||||
|
||||
const generateReloadCommands = (
|
||||
hass: HomeAssistant
|
||||
): ActionCommandComboBoxItem[] => {
|
||||
// Get all domains that have a direct "reload" service
|
||||
const reloadableDomains = componentsWithService(hass, "reload");
|
||||
|
||||
const commands = reloadableDomains.map((domain) => ({
|
||||
primary:
|
||||
hass.localize(`ui.dialogs.quick-bar.commands.reload.${domain}`) ||
|
||||
hass.localize("ui.dialogs.quick-bar.commands.reload.reload", {
|
||||
domain: domainToName(hass.localize, domain),
|
||||
}),
|
||||
domain,
|
||||
action: "reload",
|
||||
icon_path: mdiReload,
|
||||
secondary: hass.localize(`ui.dialogs.quick-bar.commands.types.reload`),
|
||||
}));
|
||||
|
||||
// Add "frontend.reload_themes"
|
||||
commands.push({
|
||||
primary: hass.localize("ui.dialogs.quick-bar.commands.reload.themes"),
|
||||
domain: "frontend",
|
||||
action: "reload_themes",
|
||||
icon_path: mdiReload,
|
||||
secondary: hass.localize("ui.dialogs.quick-bar.commands.types.reload"),
|
||||
});
|
||||
|
||||
// Add "homeassistant.reload_core_config"
|
||||
commands.push({
|
||||
primary: hass.localize("ui.dialogs.quick-bar.commands.reload.core"),
|
||||
domain: "homeassistant",
|
||||
action: "reload_core_config",
|
||||
icon_path: mdiReload,
|
||||
secondary: hass.localize("ui.dialogs.quick-bar.commands.types.reload"),
|
||||
});
|
||||
|
||||
// Add "homeassistant.reload_all"
|
||||
commands.push({
|
||||
primary: hass.localize("ui.dialogs.quick-bar.commands.reload.all"),
|
||||
domain: "homeassistant",
|
||||
action: "reload_all",
|
||||
icon_path: mdiReload,
|
||||
secondary: hass.localize("ui.dialogs.quick-bar.commands.types.reload"),
|
||||
});
|
||||
|
||||
return commands.map((command, index) => ({
|
||||
...command,
|
||||
id: `command_${index}_${command.primary}`,
|
||||
sorting_label: `${command.primary}_${command.secondary}_${command.domain}`,
|
||||
}));
|
||||
};
|
||||
|
||||
const generateServerControlCommands = (
|
||||
hass: HomeAssistant
|
||||
): ActionCommandComboBoxItem[] => {
|
||||
const serverActions = ["restart", "stop"] as const;
|
||||
|
||||
return serverActions.map((action, index) => {
|
||||
const primary = hass.localize(
|
||||
"ui.dialogs.quick-bar.commands.server_control.perform_action",
|
||||
{
|
||||
action: hass.localize(
|
||||
`ui.dialogs.quick-bar.commands.server_control.${action}`
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
const secondary = hass.localize(
|
||||
"ui.dialogs.quick-bar.commands.types.server_control"
|
||||
);
|
||||
|
||||
return {
|
||||
id: `server_control_${index}_${action}`,
|
||||
primary,
|
||||
domain: "homeassistant",
|
||||
icon_path: mdiServerNetwork,
|
||||
secondary,
|
||||
sorting_label: `${primary}_${secondary}_${action}`,
|
||||
action,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const generateActionCommands = (
|
||||
hass: HomeAssistant
|
||||
): ActionCommandComboBoxItem[] => [
|
||||
...generateReloadCommands(hass),
|
||||
...generateServerControlCommands(hass),
|
||||
];
|
||||
|
||||
export const commandComboBoxKeys: FuseWeightedKey[] = [
|
||||
{
|
||||
name: "primary",
|
||||
weight: 10,
|
||||
},
|
||||
{
|
||||
name: "domain",
|
||||
weight: 8,
|
||||
},
|
||||
{
|
||||
name: "secondary",
|
||||
weight: 6,
|
||||
},
|
||||
];
|
||||
|
||||
export const navigateComboBoxKeys: FuseWeightedKey[] = [
|
||||
{
|
||||
name: "primary",
|
||||
weight: 10,
|
||||
},
|
||||
{
|
||||
name: "path",
|
||||
weight: 8,
|
||||
},
|
||||
{
|
||||
name: "secondary",
|
||||
weight: 6,
|
||||
},
|
||||
];
|
||||
@@ -3,8 +3,8 @@ import { computeDomain } from "../common/entity/compute_domain";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-device-picker";
|
||||
import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { AreaRegistryEntry } from "./area/area_registry";
|
||||
import type { FloorComboBoxItem } from "./area_floor_picker";
|
||||
import type { AreaRegistryEntry } from "./area_registry";
|
||||
import type { DevicePickerItem } from "./device/device_picker";
|
||||
import type { DeviceRegistryEntry } from "./device/device_registry";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "./entity/entity";
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Connection } from "home-assistant-js-websocket";
|
||||
import { createCollection } from "home-assistant-js-websocket";
|
||||
import type { Store } from "home-assistant-js-websocket/dist/store";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import type { AreaRegistryEntry } from "./area_registry";
|
||||
import type { AreaRegistryEntry } from "./area/area_registry";
|
||||
|
||||
const fetchAreaRegistry = (conn: Connection) =>
|
||||
conn.sendMessagePromise<AreaRegistryEntry[]>({
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,15 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
|
||||
export const enum QuickBarMode {
|
||||
Command = "command",
|
||||
Device = "device",
|
||||
Entity = "entity",
|
||||
}
|
||||
export type QuickBarSection =
|
||||
| "entity"
|
||||
| "device"
|
||||
| "area"
|
||||
| "navigate"
|
||||
| "command";
|
||||
|
||||
export interface QuickBarParams {
|
||||
entityFilter?: string;
|
||||
mode?: QuickBarMode;
|
||||
mode?: QuickBarSection;
|
||||
hint?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { mdiAppleKeyboardCommand } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
import "../../components/ha-alert";
|
||||
import { createCloseHeading } from "../../components/ha-dialog";
|
||||
import "../../components/ha-wa-dialog";
|
||||
import "../../components/ha-svg-icon";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { isMac } from "../../util/is_mac";
|
||||
|
||||
@@ -39,6 +38,10 @@ const _SHORTCUTS: Section[] = [
|
||||
{
|
||||
textTranslationKey: "ui.dialogs.shortcuts.searching.on_any_page",
|
||||
},
|
||||
{
|
||||
shortcut: [CTRL_CMD, "K"],
|
||||
descriptionTranslationKey: "ui.dialogs.shortcuts.searching.search",
|
||||
},
|
||||
{
|
||||
shortcut: ["C"],
|
||||
descriptionTranslationKey:
|
||||
@@ -162,17 +165,22 @@ const _SHORTCUTS: Section[] = [
|
||||
class DialogShortcuts extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _opened = false;
|
||||
@state() private _open = false;
|
||||
|
||||
public async showDialog(): Promise<void> {
|
||||
this._opened = true;
|
||||
this._open = true;
|
||||
}
|
||||
|
||||
public async closeDialog(): Promise<void> {
|
||||
this._opened = false;
|
||||
private _dialogClosed() {
|
||||
this._open = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
public async closeDialog() {
|
||||
this._open = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private _renderShortcut(
|
||||
shortcutKeys: ShortcutString[],
|
||||
descriptionKey: LocalizeKeys
|
||||
@@ -201,20 +209,11 @@ class DialogShortcuts extends LitElement {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._opened) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this.closeDialog}
|
||||
defaultAction="ignore"
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.dialogs.shortcuts.title")
|
||||
)}
|
||||
<ha-wa-dialog
|
||||
.open=${this._open}
|
||||
@closed=${this._dialogClosed}
|
||||
.headerTitle=${this.hass.localize("ui.dialogs.shortcuts.title")}
|
||||
>
|
||||
<div class="content">
|
||||
${_SHORTCUTS.map(
|
||||
@@ -237,7 +236,7 @@ class DialogShortcuts extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ha-alert>
|
||||
<ha-alert slot="footer">
|
||||
${this.hass.localize("ui.dialogs.shortcuts.enable_shortcuts_hint", {
|
||||
user_profile: html`<a href="/profile/general#shortcuts"
|
||||
>${this.hass.localize(
|
||||
@@ -246,25 +245,12 @@ class DialogShortcuts extends LitElement {
|
||||
>`,
|
||||
})}
|
||||
</ha-alert>
|
||||
</ha-dialog>
|
||||
</ha-wa-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--dialog-z-index: 15;
|
||||
}
|
||||
|
||||
h3:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -286,6 +272,10 @@ class DialogShortcuts extends LitElement {
|
||||
ha-svg-icon {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
ha-alert a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import "../../../components/ha-wa-dialog";
|
||||
import type {
|
||||
AreaRegistryEntry,
|
||||
AreaRegistryEntryMutableParams,
|
||||
} from "../../../data/area_registry";
|
||||
import { deleteAreaRegistryEntry } from "../../../data/area_registry";
|
||||
} from "../../../data/area/area_registry";
|
||||
import { deleteAreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import {
|
||||
SENSOR_DEVICE_CLASS_HUMIDITY,
|
||||
SENSOR_DEVICE_CLASS_TEMPERATURE,
|
||||
|
||||
@@ -14,16 +14,16 @@ import "../../../components/ha-button";
|
||||
import "../../../components/ha-dialog-footer";
|
||||
import "../../../components/ha-floor-icon";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-wa-dialog";
|
||||
import "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-sortable";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import "../../../components/ha-wa-dialog";
|
||||
import type { AreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import {
|
||||
reorderAreaRegistryEntries,
|
||||
updateAreaRegistryEntry,
|
||||
} from "../../../data/area_registry";
|
||||
} from "../../../data/area/area_registry";
|
||||
import { reorderFloorRegistryEntries } from "../../../data/floor_registry";
|
||||
import { haStyle, haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
@@ -18,7 +18,7 @@ import "../../../components/ha-settings-row";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-textfield";
|
||||
import "../../../components/ha-wa-dialog";
|
||||
import { updateAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import { updateAreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type {
|
||||
FloorRegistryEntry,
|
||||
FloorRegistryEntryMutableParams,
|
||||
|
||||
@@ -23,11 +23,11 @@ import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-list";
|
||||
import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-tooltip";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import {
|
||||
deleteAreaRegistryEntry,
|
||||
updateAreaRegistryEntry,
|
||||
} from "../../../data/area_registry";
|
||||
} from "../../../data/area/area_registry";
|
||||
import type { AutomationEntity } from "../../../data/automation";
|
||||
import { fullEntitiesContext } from "../../../data/context";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
|
||||
|
||||
@@ -31,12 +31,12 @@ import "../../../components/ha-list-item";
|
||||
import "../../../components/ha-sortable";
|
||||
import type { HaSortableOptions } from "../../../components/ha-sortable";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import {
|
||||
createAreaRegistryEntry,
|
||||
reorderAreaRegistryEntries,
|
||||
updateAreaRegistryEntry,
|
||||
} from "../../../data/area_registry";
|
||||
} from "../../../data/area/area_registry";
|
||||
import type { FloorRegistryEntry } from "../../../data/floor_registry";
|
||||
import {
|
||||
createFloorRegistryEntry,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import type {
|
||||
AreaRegistryEntry,
|
||||
AreaRegistryEntryMutableParams,
|
||||
} from "../../../data/area_registry";
|
||||
} from "../../../data/area/area_registry";
|
||||
|
||||
export interface AreaRegistryDetailDialogParams {
|
||||
entry?: AreaRegistryEntry;
|
||||
|
||||
@@ -57,11 +57,11 @@ import {
|
||||
ACTION_COLLECTIONS,
|
||||
ACTION_ICONS,
|
||||
} from "../../../data/action";
|
||||
import type { FloorComboBoxItem } from "../../../data/area_floor_picker";
|
||||
import {
|
||||
getAreaDeviceLookup,
|
||||
getAreaEntityLookup,
|
||||
} from "../../../data/area_registry";
|
||||
} from "../../../data/area/area_registry";
|
||||
import type { FloorComboBoxItem } from "../../../data/area_floor_picker";
|
||||
import {
|
||||
DYNAMIC_PREFIX,
|
||||
getValueFromDynamic,
|
||||
|
||||
@@ -28,6 +28,10 @@ import "../../../../components/ha-md-list-item";
|
||||
import "../../../../components/ha-section-title";
|
||||
import "../../../../components/ha-state-icon";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
getAreaDeviceLookup,
|
||||
getAreaEntityLookup,
|
||||
} from "../../../../data/area/area_registry";
|
||||
import {
|
||||
getAreasNestedInFloors,
|
||||
type AreaFloorValue,
|
||||
@@ -35,10 +39,6 @@ import {
|
||||
type FloorNestedComboBoxItem,
|
||||
type UnassignedAreasFloorComboBoxItem,
|
||||
} from "../../../../data/area_floor_picker";
|
||||
import {
|
||||
getAreaDeviceLookup,
|
||||
getAreaEntityLookup,
|
||||
} from "../../../../data/area_registry";
|
||||
import {
|
||||
getConfigEntries,
|
||||
type ConfigEntry,
|
||||
|
||||
@@ -66,7 +66,7 @@ import type { HaMdMenuItem } from "../../../components/ha-md-menu-item";
|
||||
import "../../../components/ha-sub-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-tooltip";
|
||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import { createAreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type { AutomationEntity } from "../../../data/automation";
|
||||
import {
|
||||
deleteAutomation,
|
||||
|
||||
@@ -33,10 +33,7 @@ import {
|
||||
checkForEntityUpdates,
|
||||
filterUpdateEntitiesWithInstall,
|
||||
} from "../../../data/update";
|
||||
import {
|
||||
QuickBarMode,
|
||||
showQuickBar,
|
||||
} from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
|
||||
import { showShortcutsDialog } from "../../../dialogs/shortcuts/show-shortcuts-dialog";
|
||||
import type { PageNavigation } from "../../../layouts/hass-tabs-subpage";
|
||||
@@ -364,7 +361,6 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
};
|
||||
|
||||
showQuickBar(this, {
|
||||
mode: QuickBarMode.Command,
|
||||
hint: this.hass.enableShortcuts
|
||||
? this.hass.localize("ui.dialogs.quick-bar.key_c_tip", params)
|
||||
: undefined,
|
||||
|
||||
@@ -54,7 +54,7 @@ import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-md-divider";
|
||||
import "../../../components/ha-md-menu-item";
|
||||
import "../../../components/ha-sub-menu";
|
||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import { createAreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type { ConfigEntry, SubEntry } from "../../../data/config_entries";
|
||||
import { getSubEntries, sortConfigEntries } from "../../../data/config_entries";
|
||||
import { fullEntitiesContext } from "../../../data/context";
|
||||
|
||||
@@ -60,7 +60,7 @@ import "../../../components/ha-state-icon";
|
||||
import "../../../components/ha-sub-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-tooltip";
|
||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import { createAreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type { CategoryRegistryEntry } from "../../../data/category_registry";
|
||||
import {
|
||||
createCategoryRegistryEntry,
|
||||
|
||||
@@ -61,7 +61,7 @@ import "../../../components/ha-md-menu-item";
|
||||
import "../../../components/ha-sub-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-tooltip";
|
||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||
import { createAreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import type { CategoryRegistryEntry } from "../../../data/category_registry";
|
||||
import {
|
||||
createCategoryRegistryEntry,
|
||||
|
||||
@@ -16,7 +16,7 @@ import "../../../components/ha-control-button";
|
||||
import "../../../components/ha-control-button-group";
|
||||
import "../../../components/ha-domain-icon";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../data/area/area_registry";
|
||||
import { forwardHaptic } from "../../../data/haptics";
|
||||
import { computeCssVariable } from "../../../resources/css-variables";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
@@ -51,7 +51,7 @@ import "../../components/ha-svg-icon";
|
||||
import "../../components/ha-tab-group";
|
||||
import "../../components/ha-tab-group-tab";
|
||||
import "../../components/ha-tooltip";
|
||||
import { createAreaRegistryEntry } from "../../data/area_registry";
|
||||
import { createAreaRegistryEntry } from "../../data/area/area_registry";
|
||||
import type { LovelacePanelConfig } from "../../data/lovelace";
|
||||
import type {
|
||||
LovelaceConfig,
|
||||
@@ -72,10 +72,7 @@ import {
|
||||
showConfirmationDialog,
|
||||
} from "../../dialogs/generic/show-dialog-box";
|
||||
import { showMoreInfoDialog } from "../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import {
|
||||
QuickBarMode,
|
||||
showQuickBar,
|
||||
} from "../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showQuickBar } from "../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showShortcutsDialog } from "../../dialogs/shortcuts/show-shortcuts-dialog";
|
||||
import { showVoiceCommandDialog } from "../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
@@ -298,13 +295,11 @@ class HUIRoot extends LitElement {
|
||||
},
|
||||
{
|
||||
icon: mdiMagnify,
|
||||
key: "ui.panel.lovelace.menu.search_entities",
|
||||
key: "ui.common.search",
|
||||
buttonAction: this._showQuickBar,
|
||||
overflowAction: this._handleShowQuickBar,
|
||||
visible: !this._editMode,
|
||||
overflow: this.narrow,
|
||||
suffix:
|
||||
this.hass.enableShortcuts && !isMobileClient ? "(E)" : undefined,
|
||||
},
|
||||
{
|
||||
icon: mdiCommentProcessingOutline,
|
||||
@@ -903,7 +898,6 @@ class HUIRoot extends LitElement {
|
||||
};
|
||||
|
||||
showQuickBar(this, {
|
||||
mode: QuickBarMode.Entity,
|
||||
hint: this.hass.enableShortcuts
|
||||
? this.hass.localize("ui.tips.key_e_tip", params)
|
||||
: undefined,
|
||||
|
||||
@@ -13,11 +13,12 @@ import "../../../../../components/ha-entities-display-editor";
|
||||
import "../../../../../components/ha-icon";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button-prev";
|
||||
import type { DisplayItem } from "../../../../../components/ha-items-display-editor";
|
||||
import "../../../../../components/ha-svg-icon";
|
||||
import {
|
||||
updateAreaRegistryEntry,
|
||||
type AreaRegistryEntry,
|
||||
} from "../../../../../data/area_registry";
|
||||
} from "../../../../../data/area/area_registry";
|
||||
import {
|
||||
haCardSizeLarge,
|
||||
haCardSizeSmall,
|
||||
@@ -33,7 +34,6 @@ import {
|
||||
AREA_STRATEGY_GROUPS,
|
||||
getAreaGroupedEntities,
|
||||
} from "../helpers/areas-strategy-helper";
|
||||
import type { DisplayItem } from "../../../../../components/ha-items-display-editor";
|
||||
|
||||
@customElement("hui-areas-dashboard-strategy-editor")
|
||||
export class HuiAreasDashboardStrategyEditor
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { EntityFilterFunc } from "../../../../../common/entity/entity_filte
|
||||
import { generateEntityFilter } from "../../../../../common/entity/entity_filter";
|
||||
import { stripPrefixFromEntityName } from "../../../../../common/entity/strip_prefix_from_entity_name";
|
||||
import { orderCompare } from "../../../../../common/string/compare";
|
||||
import type { AreaRegistryEntry } from "../../../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../../../data/area/area_registry";
|
||||
import type { FloorRegistryEntry } from "../../../../../data/floor_registry";
|
||||
import type { LovelaceCardConfig } from "../../../../../data/lovelace/config/card";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
generateEntityFilter,
|
||||
} from "../../../../common/entity/entity_filter";
|
||||
import { floorDefaultIcon } from "../../../../components/ha-floor-icon";
|
||||
import type { AreaRegistryEntry } from "../../../../data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../../data/area/area_registry";
|
||||
import { getEnergyPreferences } from "../../../../data/energy";
|
||||
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
|
||||
import type {
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { promiseTimeout } from "../common/util/promise-timeout";
|
||||
import { subscribeAreaRegistry } from "../data/area_registry";
|
||||
import { subscribeAreaRegistry } from "../data/area/area_registry";
|
||||
import { broadcastConnectionStatus } from "../data/connection-status";
|
||||
import { subscribeDeviceRegistry } from "../data/device/device_registry";
|
||||
import {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
import { canOverrideAlphanumericInput } from "../common/dom/can-override-input";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import type { QuickBarParams } from "../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import {
|
||||
QuickBarMode,
|
||||
showQuickBar,
|
||||
import { ShortcutManager } from "../common/keyboard/shortcuts";
|
||||
import { extractSearchParamsObject } from "../common/url/search-params";
|
||||
import type {
|
||||
QuickBarParams,
|
||||
QuickBarSection,
|
||||
} from "../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showQuickBar } from "../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showShortcutsDialog } from "../dialogs/shortcuts/show-shortcuts-dialog";
|
||||
import { showVoiceCommandDialog } from "../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||
import type { Redirects } from "../panels/my/ha-panel-my";
|
||||
import type { Constructor, HomeAssistant } from "../types";
|
||||
import { storeState } from "../util/ha-pref-storage";
|
||||
import { showToast } from "../util/toast";
|
||||
import type { HassElement } from "./hass-element";
|
||||
import { ShortcutManager } from "../common/keyboard/shortcuts";
|
||||
import { extractSearchParamsObject } from "../common/url/search-params";
|
||||
import { showVoiceCommandDialog } from "../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||
import { canOverrideAlphanumericInput } from "../common/dom/can-override-input";
|
||||
import { showShortcutsDialog } from "../dialogs/shortcuts/show-shortcuts-dialog";
|
||||
import type { Redirects } from "../panels/my/ha-panel-my";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -39,13 +39,13 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
||||
mainWindow.addEventListener("hass-quick-bar-trigger", (ev) => {
|
||||
switch (ev.detail.key) {
|
||||
case "e":
|
||||
this._showQuickBar(ev.detail);
|
||||
this._showQuickBar(ev.detail, "entity");
|
||||
break;
|
||||
case "c":
|
||||
this._showQuickBar(ev.detail, QuickBarMode.Command);
|
||||
this._showQuickBar(ev.detail, "command");
|
||||
break;
|
||||
case "d":
|
||||
this._showQuickBar(ev.detail, QuickBarMode.Device);
|
||||
this._showQuickBar(ev.detail, "device");
|
||||
break;
|
||||
case "m":
|
||||
this._createMyLink(ev.detail);
|
||||
@@ -65,19 +65,21 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
||||
const shortcutManager = new ShortcutManager();
|
||||
shortcutManager.add({
|
||||
// Those are for latin keyboards that have e, c, m keys
|
||||
e: { handler: (ev) => this._showQuickBar(ev) },
|
||||
c: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) },
|
||||
e: { handler: (ev) => this._showQuickBar(ev, "entity") },
|
||||
c: { handler: (ev) => this._showQuickBar(ev, "command") },
|
||||
m: { handler: (ev) => this._createMyLink(ev) },
|
||||
a: { handler: (ev) => this._showVoiceCommandDialog(ev) },
|
||||
d: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
|
||||
d: { handler: (ev) => this._showQuickBar(ev, "device") },
|
||||
"$mod+k": { handler: (ev) => this._showQuickBar(ev) },
|
||||
// Workaround see https://github.com/jamiebuilds/tinykeys/issues/130
|
||||
"Shift+?": { handler: (ev) => this._showShortcutDialog(ev) },
|
||||
// Those are fallbacks for non-latin keyboards that don't have e, c, m keys (qwerty-based shortcuts)
|
||||
KeyE: { handler: (ev) => this._showQuickBar(ev) },
|
||||
KeyC: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Command) },
|
||||
KeyE: { handler: (ev) => this._showQuickBar(ev, "entity") },
|
||||
KeyC: { handler: (ev) => this._showQuickBar(ev, "command") },
|
||||
KeyM: { handler: (ev) => this._createMyLink(ev) },
|
||||
KeyA: { handler: (ev) => this._showVoiceCommandDialog(ev) },
|
||||
KeyD: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
|
||||
KeyD: { handler: (ev) => this._showQuickBar(ev, "device") },
|
||||
"$mod+KeyK": { handler: (ev) => this._showQuickBar(ev) },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,10 +104,7 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
|
||||
showVoiceCommandDialog(this, this.hass!, { pipeline_id: "last_used" });
|
||||
}
|
||||
|
||||
private _showQuickBar(
|
||||
e: KeyboardEvent,
|
||||
mode: QuickBarMode = QuickBarMode.Entity
|
||||
) {
|
||||
private _showQuickBar(e: KeyboardEvent, mode?: QuickBarSection) {
|
||||
if (!this._canShowQuickBar(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1338,6 +1338,8 @@
|
||||
"text": "Home Assistant is running in safe mode, custom integrations and frontend modules are not available. Restart Home Assistant to exit safe mode."
|
||||
},
|
||||
"quick-bar": {
|
||||
"commands_title": "Commands",
|
||||
"navigate_title": "Navigate",
|
||||
"commands": {
|
||||
"reload": {
|
||||
"all": "[%key:ui::panel::developer-tools::tabs::yaml::section::reloading::all%]",
|
||||
@@ -2148,6 +2150,7 @@
|
||||
"title": "Searching",
|
||||
"on_any_page": "On any page",
|
||||
"on_pages_with_tables": "On pages with tables",
|
||||
"search": "Search Home Assistant",
|
||||
"search_command": "search command",
|
||||
"search_entities": "search entities",
|
||||
"search_devices": "search devices",
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
EntityNameOptions,
|
||||
} from "./common/entity/compute_entity_name_display";
|
||||
import type { LocalizeFunc } from "./common/translations/localize";
|
||||
import type { AreaRegistryEntry } from "./data/area_registry";
|
||||
import type { AreaRegistryEntry } from "./data/area/area_registry";
|
||||
import type { DeviceRegistryEntry } from "./data/device/device_registry";
|
||||
import type { EntityRegistryDisplayEntry } from "./data/entity/entity_registry";
|
||||
import type { FloorRegistryEntry } from "./data/floor_registry";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { computeAreaName } from "../../../src/common/entity/compute_area_name";
|
||||
import type { AreaRegistryEntry } from "../../../src/data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../src/data/area/area_registry";
|
||||
|
||||
describe("computeAreaName", () => {
|
||||
it("returns the trimmed name if present", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area/area_registry";
|
||||
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
|
||||
import type {
|
||||
EntityRegistryDisplayEntry,
|
||||
|
||||
Reference in New Issue
Block a user