Compare commits

...

3 Commits

Author SHA1 Message Date
Aidan Timson 0c16c78984 Remove entry ids and just use integration pages 2025-12-11 14:32:27 +00:00
Aidan Timson 767d43021f Fix 2025-12-11 14:31:33 +00:00
Aidan Timson fbeab4e151 Add integrations quick bar 2025-12-11 14:28:40 +00:00
4 changed files with 120 additions and 3 deletions
+109 -3
View File
@@ -6,6 +6,7 @@ import {
mdiEarth, mdiEarth,
mdiKeyboard, mdiKeyboard,
mdiMagnify, mdiMagnify,
mdiPuzzle,
mdiReload, mdiReload,
mdiServerNetwork, mdiServerNetwork,
} from "@mdi/js"; } from "@mdi/js";
@@ -94,12 +95,22 @@ interface DeviceItem extends QuickBarItem {
area?: string; area?: string;
} }
interface IntegrationItem extends QuickBarItem {
domain: string;
translatedDomain: string;
}
const isCommandItem = (item: QuickBarItem): item is CommandItem => const isCommandItem = (item: QuickBarItem): item is CommandItem =>
(item as CommandItem).categoryKey !== undefined; (item as CommandItem).categoryKey !== undefined;
const isDeviceItem = (item: QuickBarItem): item is DeviceItem => const isDeviceItem = (item: QuickBarItem): item is DeviceItem =>
(item as DeviceItem).deviceId !== undefined; (item as DeviceItem).deviceId !== undefined;
const isIntegrationItem = (item: QuickBarItem): item is IntegrationItem =>
(item as DeviceItem).deviceId === undefined &&
(item as IntegrationItem).domain !== undefined &&
(item as IntegrationItem).translatedDomain !== undefined;
interface QuickBarNavigationItem extends CommandItem { interface QuickBarNavigationItem extends CommandItem {
path: string; path: string;
} }
@@ -121,6 +132,8 @@ export class QuickBar extends LitElement {
@state() private _deviceItems?: DeviceItem[]; @state() private _deviceItems?: DeviceItem[];
@state() private _integrationItems?: IntegrationItem[];
@state() private _filter = ""; @state() private _filter = "";
@state() private _search = ""; @state() private _search = "";
@@ -159,6 +172,8 @@ export class QuickBar extends LitElement {
this._search = ""; this._search = "";
this._entityItems = undefined; this._entityItems = undefined;
this._commandItems = undefined; this._commandItems = undefined;
this._deviceItems = undefined;
this._integrationItems = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName }); fireEvent(this, "dialog-closed", { dialog: this.localName });
} }
@@ -174,6 +189,7 @@ export class QuickBar extends LitElement {
commandItems, commandItems,
entityItems, entityItems,
deviceItems, deviceItems,
integrationItems,
filter: string filter: string
) => { ) => {
let items = entityItems; let items = entityItems;
@@ -182,6 +198,8 @@ export class QuickBar extends LitElement {
items = commandItems; items = commandItems;
} else if (mode === QuickBarMode.Device) { } else if (mode === QuickBarMode.Device) {
items = deviceItems; items = deviceItems;
} else if (mode === QuickBarMode.Integration) {
items = integrationItems;
} }
if (items && filter && filter !== " ") { if (items && filter && filter !== " ") {
@@ -201,25 +219,37 @@ export class QuickBar extends LitElement {
this._commandItems, this._commandItems,
this._entityItems, this._entityItems,
this._deviceItems, this._deviceItems,
this._integrationItems,
this._filter this._filter
); );
const translationKey = const translationKey =
this._mode === QuickBarMode.Device this._mode === QuickBarMode.Device
? "filter_placeholder_devices" ? "filter_placeholder_devices"
: "filter_placeholder"; : this._mode === QuickBarMode.Integration
? "filter_placeholder_integrations"
: "filter_placeholder";
const placeholder = this.hass.localize( const placeholder = this.hass.localize(
`ui.dialogs.quick-bar.${translationKey}` `ui.dialogs.quick-bar.${translationKey}`
); );
const commandMode = this._mode === QuickBarMode.Command; const commandMode = this._mode === QuickBarMode.Command;
const deviceMode = this._mode === QuickBarMode.Device; const deviceMode = this._mode === QuickBarMode.Device;
const integrationMode = this._mode === QuickBarMode.Integration;
const icon = commandMode const icon = commandMode
? mdiConsoleLine ? mdiConsoleLine
: deviceMode : deviceMode
? mdiDevices ? mdiDevices
: mdiMagnify; : integrationMode
const searchPrefix = commandMode ? ">" : deviceMode ? "#" : ""; ? mdiPuzzle
: mdiMagnify;
const searchPrefix = commandMode
? ">"
: deviceMode
? "#"
: integrationMode
? "@"
: "";
return html` return html`
<ha-dialog <ha-dialog
@@ -318,6 +348,9 @@ export class QuickBar extends LitElement {
} else if (this._mode === QuickBarMode.Device) { } else if (this._mode === QuickBarMode.Device) {
this._deviceItems = this._deviceItems =
this._deviceItems || (await this._generateDeviceItems()); this._deviceItems || (await this._generateDeviceItems());
} else if (this._mode === QuickBarMode.Integration) {
this._integrationItems =
this._integrationItems || (await this._generateIntegrationItems());
} else { } else {
this._entityItems = this._entityItems =
this._entityItems || (await this._generateEntityItems()); this._entityItems || (await this._generateEntityItems());
@@ -352,6 +385,10 @@ export class QuickBar extends LitElement {
return this._renderCommandItem(item, index); return this._renderCommandItem(item, index);
} }
if (isIntegrationItem(item)) {
return this._renderIntegrationItem(item, index);
}
return this._renderEntityItem(item as EntityItem, index); return this._renderEntityItem(item as EntityItem, index);
}; };
@@ -459,6 +496,35 @@ export class QuickBar extends LitElement {
`; `;
} }
private _renderIntegrationItem(item: IntegrationItem, index?: number) {
return html`
<ha-md-list-item
class="two-line"
.item=${item}
index=${ifDefined(index)}
tabindex="0"
type="button"
>
<img
slot="start"
alt=""
crossorigin="anonymous"
referrerpolicy="no-referrer"
src=${brandsUrl({
domain: item.domain,
type: "icon",
useFallback: true,
darkOptimized: this.hass.themes?.darkMode,
})}
/>
<span slot="headline">${item.primaryText}</span>
<div slot="trailing-supporting-text" class="domain">
${item.translatedDomain}
</div>
</ha-md-list-item>
`;
}
private async _processItemAndCloseDialog(item: QuickBarItem, index: number) { private async _processItemAndCloseDialog(item: QuickBarItem, index: number) {
this._addSpinnerToCommandItem(index); this._addSpinnerToCommandItem(index);
@@ -507,6 +573,9 @@ export class QuickBar extends LitElement {
} else if (newFilter.startsWith("#")) { } else if (newFilter.startsWith("#")) {
newMode = QuickBarMode.Device; newMode = QuickBarMode.Device;
newSearch = newFilter.substring(1); newSearch = newFilter.substring(1);
} else if (newFilter.startsWith("@")) {
newMode = QuickBarMode.Integration;
newSearch = newFilter.substring(1);
} else { } else {
newMode = QuickBarMode.Entity; newMode = QuickBarMode.Entity;
newSearch = newFilter; newSearch = newFilter;
@@ -706,6 +775,43 @@ export class QuickBar extends LitElement {
); );
} }
private async _generateIntegrationItems(): Promise<IntegrationItem[]> {
const configEntries = await getConfigEntries(this.hass);
// Get unique domains from enabled entries
const domains = new Set<string>();
for (const entry of configEntries) {
if (entry.disabled_by || entry.source === "ignore") {
continue;
}
domains.add(entry.domain);
}
const integrationItems: IntegrationItem[] = [];
for (const domain of domains) {
const translatedDomain = domainToName(this.hass.localize, domain);
const primaryText = translatedDomain;
integrationItems.push({
id: `integration-${domain}`,
primaryText,
domain,
translatedDomain,
action: () => navigate(`/config/integrations/integration/${domain}`),
strings: [primaryText, domain, translatedDomain],
});
}
return integrationItems.sort((a, b) =>
caseInsensitiveStringCompare(
a.primaryText,
b.primaryText,
this.hass.locale.language
)
);
}
private async _generateCommandItems(): Promise<CommandItem[]> { private async _generateCommandItems(): Promise<CommandItem[]> {
return [ return [
...(await this._generateReloadCommands()), ...(await this._generateReloadCommands()),
@@ -4,6 +4,7 @@ export const enum QuickBarMode {
Command = "command", Command = "command",
Device = "device", Device = "device",
Entity = "entity", Entity = "entity",
Integration = "integration",
} }
export interface QuickBarParams { export interface QuickBarParams {
+9
View File
@@ -47,6 +47,9 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
case "d": case "d":
this._showQuickBar(ev.detail, QuickBarMode.Device); this._showQuickBar(ev.detail, QuickBarMode.Device);
break; break;
case "i":
this._showQuickBar(ev.detail, QuickBarMode.Integration);
break;
case "m": case "m":
this._createMyLink(ev.detail); this._createMyLink(ev.detail);
break; break;
@@ -70,6 +73,9 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
m: { handler: (ev) => this._createMyLink(ev) }, m: { handler: (ev) => this._createMyLink(ev) },
a: { handler: (ev) => this._showVoiceCommandDialog(ev) }, a: { handler: (ev) => this._showVoiceCommandDialog(ev) },
d: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) }, d: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
i: {
handler: (ev) => this._showQuickBar(ev, QuickBarMode.Integration),
},
// Workaround see https://github.com/jamiebuilds/tinykeys/issues/130 // Workaround see https://github.com/jamiebuilds/tinykeys/issues/130
"Shift+?": { handler: (ev) => this._showShortcutDialog(ev) }, "Shift+?": { handler: (ev) => this._showShortcutDialog(ev) },
// Those are fallbacks for non-latin keyboards that don't have e, c, m keys (qwerty-based shortcuts) // Those are fallbacks for non-latin keyboards that don't have e, c, m keys (qwerty-based shortcuts)
@@ -78,6 +84,9 @@ export default <T extends Constructor<HassElement>>(superClass: T) =>
KeyM: { handler: (ev) => this._createMyLink(ev) }, KeyM: { handler: (ev) => this._createMyLink(ev) },
KeyA: { handler: (ev) => this._showVoiceCommandDialog(ev) }, KeyA: { handler: (ev) => this._showVoiceCommandDialog(ev) },
KeyD: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) }, KeyD: { handler: (ev) => this._showQuickBar(ev, QuickBarMode.Device) },
KeyI: {
handler: (ev) => this._showQuickBar(ev, QuickBarMode.Integration),
},
}); });
} }
+1
View File
@@ -1407,6 +1407,7 @@
}, },
"filter_placeholder": "Search entities", "filter_placeholder": "Search entities",
"filter_placeholder_devices": "Search devices", "filter_placeholder_devices": "Search devices",
"filter_placeholder_integrations": "Search integrations",
"title": "Quick search", "title": "Quick search",
"key_c_tip": "[%key:ui::tips::key_c_tip%]", "key_c_tip": "[%key:ui::tips::key_c_tip%]",
"nothing_found": "Nothing found!" "nothing_found": "Nothing found!"