mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Reorder config entries in device pages (#17105)
This commit is contained in:
parent
d6f8941098
commit
23ac7501b3
@ -1,6 +1,6 @@
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { IntegrationType } from "./integration";
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { IntegrationManifest, IntegrationType } from "./integration";
|
||||
|
||||
export interface ConfigEntry {
|
||||
entry_id: string;
|
||||
@ -143,3 +143,23 @@ export const enableConfigEntry = (hass: HomeAssistant, configEntryId: string) =>
|
||||
entry_id: configEntryId,
|
||||
disabled_by: null,
|
||||
});
|
||||
|
||||
export const sortConfigEntries = (
|
||||
configEntries: ConfigEntry[],
|
||||
manifestLookup: { [domain: string]: IntegrationManifest }
|
||||
): ConfigEntry[] => {
|
||||
const sortedConfigEntries = [...configEntries];
|
||||
|
||||
const getScore = (entry: ConfigEntry) => {
|
||||
const manifest = manifestLookup[entry.domain] as
|
||||
| IntegrationManifest
|
||||
| undefined;
|
||||
const isHelper = manifest?.integration_type === "helper";
|
||||
return isHelper ? -1 : 1;
|
||||
};
|
||||
|
||||
const configEntriesCompare = (a: ConfigEntry, b: ConfigEntry) =>
|
||||
getScore(b) - getScore(a);
|
||||
|
||||
return sortedConfigEntries.sort(configEntriesCompare);
|
||||
};
|
||||
|
@ -41,6 +41,7 @@ import {
|
||||
ConfigEntry,
|
||||
disableConfigEntry,
|
||||
DisableConfigEntryResult,
|
||||
sortConfigEntries,
|
||||
} from "../../../data/config_entries";
|
||||
import {
|
||||
computeDeviceName,
|
||||
@ -60,7 +61,7 @@ import {
|
||||
findBatteryEntity,
|
||||
updateEntityRegistryEntry,
|
||||
} from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { IntegrationManifest, domainToName } from "../../../data/integration";
|
||||
import { SceneEntities, showSceneEditor } from "../../../data/scene";
|
||||
import { findRelated, RelatedResult } from "../../../data/search";
|
||||
import {
|
||||
@ -115,6 +116,8 @@ export class HaConfigDevicePage extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public areas!: AreaRegistryEntry[];
|
||||
|
||||
@property({ attribute: false }) public manifests!: IntegrationManifest[];
|
||||
|
||||
@property() public deviceId!: string;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
|
||||
@ -145,14 +148,24 @@ export class HaConfigDevicePage extends LitElement {
|
||||
);
|
||||
|
||||
private _integrations = memoizeOne(
|
||||
(device: DeviceRegistryEntry, entries: ConfigEntry[]): ConfigEntry[] => {
|
||||
(
|
||||
device: DeviceRegistryEntry,
|
||||
entries: ConfigEntry[],
|
||||
manifests: IntegrationManifest[]
|
||||
): ConfigEntry[] => {
|
||||
const entryLookup: { [entryId: string]: ConfigEntry } = {};
|
||||
for (const entry of entries) {
|
||||
entryLookup[entry.entry_id] = entry;
|
||||
}
|
||||
return device.config_entries
|
||||
.map((entry) => entryLookup[entry])
|
||||
.filter(Boolean);
|
||||
const manifestLookup: { [domain: string]: IntegrationManifest } = {};
|
||||
for (const manifest of manifests) {
|
||||
manifestLookup[manifest.domain] = manifest;
|
||||
}
|
||||
const deviceEntries = device.config_entries
|
||||
.filter((entId) => entId in entryLookup)
|
||||
.map((entry) => entryLookup[entry]);
|
||||
|
||||
return sortConfigEntries(deviceEntries, manifestLookup);
|
||||
}
|
||||
);
|
||||
|
||||
@ -292,7 +305,11 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
|
||||
const deviceName = computeDeviceName(device, this.hass);
|
||||
const integrations = this._integrations(device, this.entries);
|
||||
const integrations = this._integrations(
|
||||
device,
|
||||
this.entries,
|
||||
this.manifests
|
||||
);
|
||||
const entities = this._entities(this.deviceId, this.entities);
|
||||
const entitiesByCategory = this._entitiesByCategory(entities);
|
||||
const batteryEntity = this._batteryEntity(entities);
|
||||
@ -920,7 +937,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
|
||||
let links = await Promise.all(
|
||||
this._integrations(device, this.entries).map(
|
||||
this._integrations(device, this.entries, this.manifests).map(
|
||||
async (entry): Promise<boolean | { link: string; domain: string }> => {
|
||||
if (entry.state !== "loaded") {
|
||||
return false;
|
||||
@ -983,7 +1000,8 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
|
||||
const buttons: DeviceAction[] = [];
|
||||
this._integrations(device, this.entries).forEach((entry) => {
|
||||
this._integrations(device, this.entries, this.manifests).forEach(
|
||||
(entry) => {
|
||||
if (entry.state !== "loaded" || !entry.supports_remove_device) {
|
||||
return;
|
||||
}
|
||||
@ -991,7 +1009,8 @@ export class HaConfigDevicePage extends LitElement {
|
||||
action: async () => {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
text:
|
||||
this._integrations(device, this.entries).length > 1
|
||||
this._integrations(device, this.entries, this.manifests)
|
||||
.length > 1
|
||||
? this.hass.localize(
|
||||
`ui.panel.config.devices.confirm_delete_integration`,
|
||||
{
|
||||
@ -1001,7 +1020,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
),
|
||||
}
|
||||
)
|
||||
: this.hass.localize(`ui.panel.config.devices.confirm_delete`),
|
||||
: this.hass.localize(
|
||||
`ui.panel.config.devices.confirm_delete`
|
||||
),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
@ -1017,7 +1038,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
classes: "warning",
|
||||
icon: mdiDelete,
|
||||
label:
|
||||
this._integrations(device, this.entries).length > 1
|
||||
this._integrations(device, this.entries, this.manifests).length > 1
|
||||
? this.hass.localize(
|
||||
`ui.panel.config.devices.delete_device_integration`,
|
||||
{
|
||||
@ -1026,7 +1047,8 @@ export class HaConfigDevicePage extends LitElement {
|
||||
)
|
||||
: this.hass.localize(`ui.panel.config.devices.delete_device`),
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (buttons.length > 0) {
|
||||
this._deleteButtons = buttons;
|
||||
@ -1061,9 +1083,11 @@ export class HaConfigDevicePage extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
const domains = this._integrations(device, this.entries).map(
|
||||
(int) => int.domain
|
||||
);
|
||||
const domains = this._integrations(
|
||||
device,
|
||||
this.entries,
|
||||
this.manifests
|
||||
).map((int) => int.domain);
|
||||
|
||||
if (domains.includes("mqtt")) {
|
||||
const mqtt = await import(
|
||||
@ -1103,9 +1127,11 @@ export class HaConfigDevicePage extends LitElement {
|
||||
|
||||
const deviceAlerts: DeviceAlert[] = [];
|
||||
|
||||
const domains = this._integrations(device, this.entries).map(
|
||||
(int) => int.domain
|
||||
);
|
||||
const domains = this._integrations(
|
||||
device,
|
||||
this.entries,
|
||||
this.manifests
|
||||
).map((int) => int.domain);
|
||||
|
||||
if (domains.includes("zwave_js")) {
|
||||
const zwave = await import(
|
||||
|
@ -25,7 +25,7 @@ import "../../../components/ha-check-list-item";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
import { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
import { ConfigEntry } from "../../../data/config_entries";
|
||||
import { ConfigEntry, sortConfigEntries } from "../../../data/config_entries";
|
||||
import {
|
||||
computeDeviceName,
|
||||
DeviceEntityLookup,
|
||||
@ -36,7 +36,7 @@ import {
|
||||
findBatteryChargingEntity,
|
||||
findBatteryEntity,
|
||||
} from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { IntegrationManifest, domainToName } from "../../../data/integration";
|
||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
@ -68,6 +68,8 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
|
||||
@property() public areas!: AreaRegistryEntry[];
|
||||
|
||||
@property() public manifests!: IntegrationManifest[];
|
||||
|
||||
@property() public route!: Route;
|
||||
|
||||
@state() private _searchParms = new URLSearchParams(window.location.search);
|
||||
@ -149,6 +151,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
entries: ConfigEntry[],
|
||||
entities: EntityRegistryEntry[],
|
||||
areas: AreaRegistryEntry[],
|
||||
manifests: IntegrationManifest[],
|
||||
filters: URLSearchParams,
|
||||
showDisabled: boolean,
|
||||
localize: LocalizeFunc
|
||||
@ -186,6 +189,11 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
areaLookup[area.area_id] = area;
|
||||
}
|
||||
|
||||
const manifestLookup: { [domain: string]: IntegrationManifest } = {};
|
||||
for (const manifest of manifests) {
|
||||
manifestLookup[manifest.domain] = manifest;
|
||||
}
|
||||
|
||||
let filterConfigEntry: ConfigEntry | undefined;
|
||||
|
||||
const filteredDomains = new Set<string>();
|
||||
@ -217,7 +225,14 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
outputDevices = outputDevices.filter((device) => !device.disabled_by);
|
||||
}
|
||||
|
||||
outputDevices = outputDevices.map((device) => ({
|
||||
outputDevices = outputDevices.map((device) => {
|
||||
const deviceEntries = sortConfigEntries(
|
||||
device.config_entries
|
||||
.filter((entId) => entId in entryLookup)
|
||||
.map((entId) => entryLookup[entId]),
|
||||
manifestLookup
|
||||
);
|
||||
return {
|
||||
...device,
|
||||
name: computeDeviceName(
|
||||
device,
|
||||
@ -234,21 +249,17 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
device.area_id && areaLookup[device.area_id]
|
||||
? areaLookup[device.area_id].name
|
||||
: "—",
|
||||
integration: device.config_entries.length
|
||||
? device.config_entries
|
||||
.filter((entId) => entId in entryLookup)
|
||||
integration: deviceEntries.length
|
||||
? deviceEntries
|
||||
.map(
|
||||
(entId) =>
|
||||
localize(`component.${entryLookup[entId].domain}.title`) ||
|
||||
entryLookup[entId].domain
|
||||
(entry) =>
|
||||
localize(`component.${entry.domain}.title`) || entry.domain
|
||||
)
|
||||
.join(", ")
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.devices.data_table.no_integration"
|
||||
),
|
||||
domains: device.config_entries
|
||||
.filter((entId) => entId in entryLookup)
|
||||
.map((entId) => entryLookup[entId].domain),
|
||||
domains: deviceEntries.map((entry) => entry.domain),
|
||||
battery_entity: [
|
||||
this._batteryEntity(device.id, deviceEntityLookup),
|
||||
this._batteryChargingEntity(device.id, deviceEntityLookup),
|
||||
@ -257,7 +268,8 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
this.hass.states[
|
||||
this._batteryEntity(device.id, deviceEntityLookup) || ""
|
||||
]?.state,
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
||||
this._numHiddenDevices = startLength - outputDevices.length;
|
||||
return {
|
||||
@ -429,6 +441,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
this.entries,
|
||||
this.entities,
|
||||
this.areas,
|
||||
this.manifests,
|
||||
this._searchParms,
|
||||
this._showDisabled,
|
||||
this.hass.localize
|
||||
@ -565,6 +578,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
||||
this.entries,
|
||||
this.entities,
|
||||
this.areas,
|
||||
this.manifests,
|
||||
this._searchParms,
|
||||
this._showDisabled,
|
||||
this.hass.localize
|
||||
|
@ -14,6 +14,10 @@ import {
|
||||
EntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity_registry";
|
||||
import {
|
||||
IntegrationManifest,
|
||||
fetchIntegrationManifests,
|
||||
} from "../../../data/integration";
|
||||
import {
|
||||
HassRouterPage,
|
||||
RouterOptions,
|
||||
@ -47,6 +51,8 @@ class HaConfigDevices extends HassRouterPage {
|
||||
|
||||
@state() private _configEntries: ConfigEntry[] = [];
|
||||
|
||||
@state() private _manifests: IntegrationManifest[] = [];
|
||||
|
||||
@state()
|
||||
private _entityRegistryEntries: EntityRegistryEntry[] = [];
|
||||
|
||||
@ -99,6 +105,7 @@ class HaConfigDevices extends HassRouterPage {
|
||||
|
||||
pageEl.entities = this._entityRegistryEntries;
|
||||
pageEl.entries = this._configEntries;
|
||||
pageEl.manifests = this._manifests;
|
||||
pageEl.devices = this._deviceRegistryEntries;
|
||||
pageEl.areas = this._areas;
|
||||
pageEl.narrow = this.narrow;
|
||||
@ -111,6 +118,10 @@ class HaConfigDevices extends HassRouterPage {
|
||||
getConfigEntries(this.hass).then((configEntries) => {
|
||||
this._configEntries = configEntries;
|
||||
});
|
||||
fetchIntegrationManifests(this.hass).then((manifests) => {
|
||||
this._manifests = manifests;
|
||||
});
|
||||
|
||||
if (this._unsubs) {
|
||||
return;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user