mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Add filtering and grouping to device and entities config pages (#20204)
* Add filtering and grouping to device and entities config pages * Update hass-tabs-subpage-data-table.ts * Change label * Update ha-config-voice-assistants-expose.ts * fix expose multi select * Update ha-config-voice-assistants-expose.ts
This commit is contained in:
parent
f5ff55abc5
commit
ae8671af96
@ -470,6 +470,7 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
|
|
||||||
private _disableSelectMode() {
|
private _disableSelectMode() {
|
||||||
this._selectMode = false;
|
this._selectMode = false;
|
||||||
|
this._dataTable.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleSearchChange(ev: CustomEvent) {
|
private _handleSearchChange(ev: CustomEvent) {
|
||||||
@ -665,6 +666,7 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
|
|
||||||
.select-mode-chip {
|
.select-mode-chip {
|
||||||
--md-assist-chip-icon-label-space: 0;
|
--md-assist-chip-icon-label-space: 0;
|
||||||
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-dialog {
|
ha-dialog {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||||
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import { mdiCancel, mdiFilterVariant, mdiPlus } from "@mdi/js";
|
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
LitElement,
|
LitElement,
|
||||||
@ -10,6 +9,7 @@ import {
|
|||||||
html,
|
html,
|
||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
@ -28,7 +28,12 @@ import "../../../components/entity/ha-battery-icon";
|
|||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-check-list-item";
|
import "../../../components/ha-check-list-item";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
|
import "../../../components/ha-filter-devices";
|
||||||
|
import "../../../components/ha-filter-floor-areas";
|
||||||
|
import "../../../components/ha-filter-integrations";
|
||||||
|
import "../../../components/ha-filter-states";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-alert";
|
||||||
import { ConfigEntry, sortConfigEntries } from "../../../data/config_entries";
|
import { ConfigEntry, sortConfigEntries } from "../../../data/config_entries";
|
||||||
import { fullEntitiesContext } from "../../../data/context";
|
import { fullEntitiesContext } from "../../../data/context";
|
||||||
import {
|
import {
|
||||||
@ -41,7 +46,7 @@ import {
|
|||||||
findBatteryChargingEntity,
|
findBatteryChargingEntity,
|
||||||
findBatteryEntity,
|
findBatteryEntity,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
import { IntegrationManifest, domainToName } from "../../../data/integration";
|
import { IntegrationManifest } from "../../../data/integration";
|
||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
@ -77,11 +82,14 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
|
|
||||||
@state() private _searchParms = new URLSearchParams(window.location.search);
|
@state() private _searchParms = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
@state() private _showDisabled = false;
|
|
||||||
|
|
||||||
@state() private _filter: string = history.state?.filter || "";
|
@state() private _filter: string = history.state?.filter || "";
|
||||||
|
|
||||||
@state() private _numHiddenDevices = 0;
|
@state() private _filters: Record<
|
||||||
|
string,
|
||||||
|
{ value: string[] | undefined; items: Set<string> | undefined }
|
||||||
|
> = {};
|
||||||
|
|
||||||
|
@state() private _expandedFilter?: string;
|
||||||
|
|
||||||
private _ignoreLocationChange = false;
|
private _ignoreLocationChange = false;
|
||||||
|
|
||||||
@ -104,55 +112,72 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
}
|
}
|
||||||
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
||||||
this._searchParms = new URLSearchParams(window.location.search);
|
this._searchParms = new URLSearchParams(window.location.search);
|
||||||
|
this._setFiltersFromUrl();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _popState = () => {
|
private _popState = () => {
|
||||||
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
||||||
this._searchParms = new URLSearchParams(window.location.search);
|
this._searchParms = new URLSearchParams(window.location.search);
|
||||||
|
this._setFiltersFromUrl();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _activeFilters = memoizeOne(
|
private _states = memoizeOne((localize: LocalizeFunc) => [
|
||||||
(
|
{
|
||||||
entries: ConfigEntry[],
|
value: "disabled",
|
||||||
filters: URLSearchParams,
|
label: localize("ui.panel.config.devices.data_table.disabled_by"),
|
||||||
localize: LocalizeFunc
|
},
|
||||||
): string[] | undefined => {
|
]);
|
||||||
const filterTexts: string[] = [];
|
|
||||||
filters.forEach((value, key) => {
|
firstUpdated() {
|
||||||
switch (key) {
|
this._filters = {
|
||||||
case "config_entry": {
|
"ha-filter-states": {
|
||||||
const configEntry = entries.find(
|
value: [],
|
||||||
(entry) => entry.entry_id === value
|
items: undefined,
|
||||||
);
|
},
|
||||||
if (!configEntry) {
|
};
|
||||||
break;
|
this._setFiltersFromUrl();
|
||||||
}
|
}
|
||||||
const integrationName = domainToName(localize, configEntry.domain);
|
|
||||||
filterTexts.push(
|
private _setFiltersFromUrl() {
|
||||||
`${this.hass.localize(
|
if (this._searchParms.has("domain")) {
|
||||||
"ui.panel.config.integrations.integration"
|
this._filters = {
|
||||||
)} "${integrationName}${
|
...this._filters,
|
||||||
integrationName !== configEntry.title
|
"ha-filter-states": {
|
||||||
? `: ${configEntry.title}`
|
value: [
|
||||||
: ""
|
...(this._filters["ha-filter-states"]?.value || []),
|
||||||
}"`
|
"disabled",
|
||||||
);
|
],
|
||||||
break;
|
items: undefined,
|
||||||
}
|
},
|
||||||
case "domain": {
|
"ha-filter-integrations": {
|
||||||
filterTexts.push(
|
value: [this._searchParms.get("domain")!],
|
||||||
`${this.hass.localize(
|
items: undefined,
|
||||||
"ui.panel.config.integrations.integration"
|
},
|
||||||
)} "${domainToName(localize, value)}"`
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return filterTexts.length ? filterTexts : undefined;
|
|
||||||
}
|
}
|
||||||
);
|
if (this._searchParms.has("config_entry")) {
|
||||||
|
this._filters = {
|
||||||
|
...this._filters,
|
||||||
|
"ha-filter-states": {
|
||||||
|
value: [
|
||||||
|
...(this._filters["ha-filter-states"]?.value || []),
|
||||||
|
"disabled",
|
||||||
|
],
|
||||||
|
items: undefined,
|
||||||
|
},
|
||||||
|
config_entry: {
|
||||||
|
value: [this._searchParms.get("config_entry")!],
|
||||||
|
items: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearFilter() {
|
||||||
|
this._filters = {};
|
||||||
|
}
|
||||||
|
|
||||||
private _devicesAndFilterDomains = memoizeOne(
|
private _devicesAndFilterDomains = memoizeOne(
|
||||||
(
|
(
|
||||||
@ -161,17 +186,16 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
entities: EntityRegistryEntry[],
|
entities: EntityRegistryEntry[],
|
||||||
areas: HomeAssistant["areas"],
|
areas: HomeAssistant["areas"],
|
||||||
manifests: IntegrationManifest[],
|
manifests: IntegrationManifest[],
|
||||||
filters: URLSearchParams,
|
filters: Record<
|
||||||
showDisabled: boolean,
|
string,
|
||||||
|
{ value: string[] | undefined; items: Set<string> | undefined }
|
||||||
|
>,
|
||||||
localize: LocalizeFunc
|
localize: LocalizeFunc
|
||||||
) => {
|
) => {
|
||||||
// Some older installations might have devices pointing at invalid entryIDs
|
// Some older installations might have devices pointing at invalid entryIDs
|
||||||
// So we guard for that.
|
// So we guard for that.
|
||||||
let outputDevices: DeviceRowData[] = Object.values(devices);
|
let outputDevices: DeviceRowData[] = Object.values(devices);
|
||||||
|
|
||||||
// If nothing gets filtered, this is our correct count of devices
|
|
||||||
let startLength = outputDevices.length;
|
|
||||||
|
|
||||||
const deviceEntityLookup: DeviceEntityLookup = {};
|
const deviceEntityLookup: DeviceEntityLookup = {};
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (!entity.device_id) {
|
if (!entity.device_id) {
|
||||||
@ -193,33 +217,48 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
manifestLookup[manifest.domain] = manifest;
|
manifestLookup[manifest.domain] = manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filterConfigEntry: ConfigEntry | undefined;
|
let filteredConfigEntry: ConfigEntry | undefined;
|
||||||
|
|
||||||
const filteredDomains = new Set<string>();
|
const filteredDomains = new Set<string>();
|
||||||
|
|
||||||
filters.forEach((value, key) => {
|
Object.entries(filters).forEach(([key, flter]) => {
|
||||||
if (key === "config_entry") {
|
if (key === "config_entry" && flter.value?.length) {
|
||||||
outputDevices = outputDevices.filter((device) =>
|
outputDevices = outputDevices.filter((device) =>
|
||||||
device.config_entries.includes(value)
|
device.config_entries.some((entryId) =>
|
||||||
|
flter.value?.includes(entryId)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
startLength = outputDevices.length;
|
|
||||||
filterConfigEntry = entries.find((entry) => entry.entry_id === value);
|
const configEntries = entries.filter(
|
||||||
if (filterConfigEntry) {
|
(entry) => entry.entry_id && flter.value?.includes(entry.entry_id)
|
||||||
filteredDomains.add(filterConfigEntry.domain);
|
);
|
||||||
|
|
||||||
|
configEntries.forEach((configEntry) => {
|
||||||
|
filteredDomains.add(configEntry.domain);
|
||||||
|
});
|
||||||
|
if (configEntries.length === 1) {
|
||||||
|
filteredConfigEntry = configEntries[0];
|
||||||
}
|
}
|
||||||
}
|
} else if (key === "ha-filter-integrations" && flter.value?.length) {
|
||||||
if (key === "domain") {
|
|
||||||
const entryIds = entries
|
const entryIds = entries
|
||||||
.filter((entry) => entry.domain === value)
|
.filter((entry) => flter.value!.includes(entry.domain))
|
||||||
.map((entry) => entry.entry_id);
|
.map((entry) => entry.entry_id);
|
||||||
outputDevices = outputDevices.filter((device) =>
|
outputDevices = outputDevices.filter((device) =>
|
||||||
device.config_entries.some((entryId) => entryIds.includes(entryId))
|
device.config_entries.some((entryId) => entryIds.includes(entryId))
|
||||||
);
|
);
|
||||||
startLength = outputDevices.length;
|
flter.value!.forEach((domain) => filteredDomains.add(domain));
|
||||||
filteredDomains.add(value);
|
} else if (flter.items) {
|
||||||
|
outputDevices = outputDevices.filter((device) =>
|
||||||
|
flter.items!.has(device.id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const stateFilters = filters["ha-filter-states"]?.value;
|
||||||
|
|
||||||
|
const showDisabled =
|
||||||
|
stateFilters?.length && stateFilters.includes("disabled");
|
||||||
|
|
||||||
if (!showDisabled) {
|
if (!showDisabled) {
|
||||||
outputDevices = outputDevices.filter((device) => !device.disabled_by);
|
outputDevices = outputDevices.filter((device) => !device.disabled_by);
|
||||||
}
|
}
|
||||||
@ -270,165 +309,140 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
this._numHiddenDevices = startLength - formattedOutputDevices.length;
|
|
||||||
return {
|
return {
|
||||||
devicesOutput: formattedOutputDevices,
|
devicesOutput: formattedOutputDevices,
|
||||||
filteredConfigEntry: filterConfigEntry,
|
filteredConfigEntry,
|
||||||
filteredDomains,
|
filteredDomains,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
private _columns = memoizeOne(
|
private _columns = memoizeOne((localize: LocalizeFunc, narrow: boolean) => {
|
||||||
(localize: LocalizeFunc, narrow: boolean, showDisabled: boolean) => {
|
type DeviceItem = ReturnType<
|
||||||
type DeviceItem = ReturnType<
|
typeof this._devicesAndFilterDomains
|
||||||
typeof this._devicesAndFilterDomains
|
>["devicesOutput"][number];
|
||||||
>["devicesOutput"][number];
|
|
||||||
|
|
||||||
const columns: DataTableColumnContainer<DeviceItem> = {
|
const columns: DataTableColumnContainer<DeviceItem> = {
|
||||||
icon: {
|
icon: {
|
||||||
title: "",
|
title: "",
|
||||||
type: "icon",
|
type: "icon",
|
||||||
template: (device) =>
|
template: (device) =>
|
||||||
device.domains.length
|
device.domains.length
|
||||||
? html`<img
|
? html`<img
|
||||||
alt=""
|
alt=""
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
referrerpolicy="no-referrer"
|
referrerpolicy="no-referrer"
|
||||||
src=${brandsUrl({
|
src=${brandsUrl({
|
||||||
domain: device.domains[0],
|
domain: device.domains[0],
|
||||||
type: "icon",
|
type: "icon",
|
||||||
darkOptimized: this.hass.themes?.darkMode,
|
darkOptimized: this.hass.themes?.darkMode,
|
||||||
})}
|
})}
|
||||||
/>`
|
/>`
|
||||||
: "",
|
: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (narrow) {
|
if (narrow) {
|
||||||
columns.name = {
|
columns.name = {
|
||||||
title: localize("ui.panel.config.devices.data_table.device"),
|
title: localize("ui.panel.config.devices.data_table.device"),
|
||||||
main: true,
|
main: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
grows: true,
|
grows: true,
|
||||||
template: (device) => html`
|
template: (device) => html`
|
||||||
${device.name}
|
${device.name}
|
||||||
<div class="secondary">${device.area} | ${device.integration}</div>
|
<div class="secondary">${device.area} | ${device.integration}</div>
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
columns.name = {
|
columns.name = {
|
||||||
title: localize("ui.panel.config.devices.data_table.device"),
|
title: localize("ui.panel.config.devices.data_table.device"),
|
||||||
main: true,
|
main: true,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
grows: true,
|
grows: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
columns.manufacturer = {
|
columns.manufacturer = {
|
||||||
title: localize("ui.panel.config.devices.data_table.manufacturer"),
|
title: localize("ui.panel.config.devices.data_table.manufacturer"),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
width: "15%",
|
groupable: true,
|
||||||
};
|
width: "15%",
|
||||||
columns.model = {
|
};
|
||||||
title: localize("ui.panel.config.devices.data_table.model"),
|
columns.model = {
|
||||||
sortable: true,
|
title: localize("ui.panel.config.devices.data_table.model"),
|
||||||
hidden: narrow,
|
sortable: true,
|
||||||
filterable: true,
|
hidden: narrow,
|
||||||
width: "15%",
|
filterable: true,
|
||||||
};
|
width: "15%",
|
||||||
columns.area = {
|
};
|
||||||
title: localize("ui.panel.config.devices.data_table.area"),
|
columns.area = {
|
||||||
sortable: true,
|
title: localize("ui.panel.config.devices.data_table.area"),
|
||||||
hidden: narrow,
|
sortable: true,
|
||||||
filterable: true,
|
hidden: narrow,
|
||||||
width: "15%",
|
filterable: true,
|
||||||
};
|
groupable: true,
|
||||||
columns.integration = {
|
width: "15%",
|
||||||
title: localize("ui.panel.config.devices.data_table.integration"),
|
};
|
||||||
sortable: true,
|
columns.integration = {
|
||||||
hidden: narrow,
|
title: localize("ui.panel.config.devices.data_table.integration"),
|
||||||
filterable: true,
|
sortable: true,
|
||||||
width: "15%",
|
hidden: narrow,
|
||||||
};
|
filterable: true,
|
||||||
columns.battery_entity = {
|
groupable: true,
|
||||||
title: localize("ui.panel.config.devices.data_table.battery"),
|
width: "15%",
|
||||||
sortable: true,
|
};
|
||||||
filterable: true,
|
columns.battery_entity = {
|
||||||
type: "numeric",
|
title: localize("ui.panel.config.devices.data_table.battery"),
|
||||||
width: narrow ? "105px" : "15%",
|
sortable: true,
|
||||||
maxWidth: "105px",
|
filterable: true,
|
||||||
valueColumn: "battery_level",
|
type: "numeric",
|
||||||
template: (device) => {
|
width: narrow ? "105px" : "15%",
|
||||||
const batteryEntityPair = device.battery_entity;
|
maxWidth: "105px",
|
||||||
const battery =
|
valueColumn: "battery_level",
|
||||||
batteryEntityPair && batteryEntityPair[0]
|
template: (device) => {
|
||||||
? this.hass.states[batteryEntityPair[0]]
|
const batteryEntityPair = device.battery_entity;
|
||||||
: undefined;
|
const battery =
|
||||||
const batteryDomain = battery
|
batteryEntityPair && batteryEntityPair[0]
|
||||||
? computeStateDomain(battery)
|
? this.hass.states[batteryEntityPair[0]]
|
||||||
|
: undefined;
|
||||||
|
const batteryDomain = battery ? computeStateDomain(battery) : undefined;
|
||||||
|
const batteryCharging =
|
||||||
|
batteryEntityPair && batteryEntityPair[1]
|
||||||
|
? this.hass.states[batteryEntityPair[1]]
|
||||||
: undefined;
|
: undefined;
|
||||||
const batteryCharging =
|
|
||||||
batteryEntityPair && batteryEntityPair[1]
|
|
||||||
? this.hass.states[batteryEntityPair[1]]
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return battery &&
|
return battery &&
|
||||||
(batteryDomain === "binary_sensor" || !isNaN(battery.state as any))
|
(batteryDomain === "binary_sensor" || !isNaN(battery.state as any))
|
||||||
? html`
|
? html`
|
||||||
${batteryDomain === "sensor"
|
${batteryDomain === "sensor"
|
||||||
? this.hass.formatEntityState(battery)
|
? this.hass.formatEntityState(battery)
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-battery-icon
|
<ha-battery-icon
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.batteryStateObj=${battery}
|
.batteryStateObj=${battery}
|
||||||
.batteryChargingStateObj=${batteryCharging}
|
.batteryChargingStateObj=${batteryCharging}
|
||||||
></ha-battery-icon>
|
></ha-battery-icon>
|
||||||
`
|
`
|
||||||
: html`—`;
|
: html`—`;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (showDisabled) {
|
columns.disabled_by = {
|
||||||
columns.disabled_by = {
|
title: "",
|
||||||
title: "",
|
label: localize("ui.panel.config.devices.data_table.disabled_by"),
|
||||||
label: localize("ui.panel.config.devices.data_table.disabled_by"),
|
hidden: true,
|
||||||
type: "icon",
|
template: (device) =>
|
||||||
template: (device) =>
|
device.disabled_by
|
||||||
device.disabled_by
|
? this.hass.localize("ui.panel.config.devices.disabled")
|
||||||
? html`<div
|
: "",
|
||||||
tabindex="0"
|
};
|
||||||
style="display:inline-block; position: relative;"
|
return columns;
|
||||||
>
|
});
|
||||||
<ha-svg-icon .path=${mdiCancel}></ha-svg-icon>
|
|
||||||
<simple-tooltip animation-delay="0" position="left">
|
|
||||||
${this.hass.localize("ui.panel.config.devices.disabled")}
|
|
||||||
</simple-tooltip>
|
|
||||||
</div>`
|
|
||||||
: "—",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return columns;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
public willUpdate(changedProps) {
|
|
||||||
if (changedProps.has("_searchParms")) {
|
|
||||||
if (
|
|
||||||
this._searchParms.get("config_entry") ||
|
|
||||||
this._searchParms.get("domain")
|
|
||||||
) {
|
|
||||||
// If we are requested to show the devices for a given config entry / domain,
|
|
||||||
// also show the disabled ones by default.
|
|
||||||
this._showDisabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const { devicesOutput } = this._devicesAndFilterDomains(
|
const { devicesOutput } = this._devicesAndFilterDomains(
|
||||||
@ -437,13 +451,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
this.entities,
|
this.entities,
|
||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this.manifests,
|
this.manifests,
|
||||||
this._searchParms,
|
this._filters,
|
||||||
this._showDisabled,
|
|
||||||
this.hass.localize
|
|
||||||
);
|
|
||||||
const activeFilters = this._activeFilters(
|
|
||||||
this.entries,
|
|
||||||
this._searchParms,
|
|
||||||
this.hass.localize
|
this.hass.localize
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -456,22 +464,16 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
: "/config"}
|
: "/config"}
|
||||||
.tabs=${configSections.devices}
|
.tabs=${configSections.devices}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.activeFilters=${activeFilters}
|
|
||||||
.numHidden=${this._numHiddenDevices}
|
|
||||||
.searchLabel=${this.hass.localize(
|
.searchLabel=${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.search"
|
"ui.panel.config.devices.picker.search"
|
||||||
)}
|
)}
|
||||||
.hiddenLabel=${this.hass.localize(
|
.columns=${this._columns(this.hass.localize, this.narrow)}
|
||||||
"ui.panel.config.devices.picker.filter.hidden_devices",
|
|
||||||
{ number: this._numHiddenDevices }
|
|
||||||
)}
|
|
||||||
.columns=${this._columns(
|
|
||||||
this.hass.localize,
|
|
||||||
this.narrow,
|
|
||||||
this._showDisabled
|
|
||||||
)}
|
|
||||||
.data=${devicesOutput}
|
.data=${devicesOutput}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
|
hasFilters
|
||||||
|
.filters=${Object.values(this._filters).filter(
|
||||||
|
(filter) => filter.value?.length
|
||||||
|
).length}
|
||||||
@clear-filter=${this._clearFilter}
|
@clear-filter=${this._clearFilter}
|
||||||
@search-changed=${this._handleSearchChange}
|
@search-changed=${this._handleSearchChange}
|
||||||
@row-click=${this._handleRowClicked}
|
@row-click=${this._handleRowClicked}
|
||||||
@ -490,37 +492,62 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</ha-fab>
|
</ha-fab>
|
||||||
<ha-button-menu slot="filter-menu" multi>
|
${this._filters.config_entry?.value?.length
|
||||||
<ha-icon-button
|
? html`<ha-alert slot="filter-pane">
|
||||||
slot="trigger"
|
Filtering by config entry
|
||||||
.label=${this.hass!.localize(
|
${this.entries?.find(
|
||||||
"ui.panel.config.devices.picker.filter.filter"
|
(entry) =>
|
||||||
)}
|
entry.entry_id === this._filters.config_entry!.value![0]
|
||||||
.path=${mdiFilterVariant}
|
)?.title || this._filters.config_entry.value[0]}
|
||||||
></ha-icon-button>
|
</ha-alert>`
|
||||||
${this.narrow && activeFilters?.length
|
: nothing}
|
||||||
? html`<mwc-list-item @click=${this._clearFilter}
|
<ha-filter-floor-areas
|
||||||
>${this.hass.localize("ui.components.data-table.filtering_by")}
|
.hass=${this.hass}
|
||||||
${activeFilters.join(", ")}
|
type="device"
|
||||||
<span class="clear"
|
.value=${this._filters["ha-filter-floor-areas"]?.value}
|
||||||
>${this.hass.localize("ui.common.clear")}</span
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
></mwc-list-item
|
slot="filter-pane"
|
||||||
>`
|
.expanded=${this._expandedFilter === "ha-filter-floor-areas"}
|
||||||
: ""}
|
.narrow=${this.narrow}
|
||||||
<ha-check-list-item
|
@expanded-changed=${this._filterExpanded}
|
||||||
left
|
></ha-filter-floor-areas>
|
||||||
@request-selected=${this._showDisabledChanged}
|
<ha-filter-integrations
|
||||||
.selected=${this._showDisabled}
|
.hass=${this.hass}
|
||||||
>
|
.value=${this._filters["ha-filter-integrations"]?.value}
|
||||||
${this.hass!.localize(
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
"ui.panel.config.devices.picker.filter.show_disabled"
|
slot="filter-pane"
|
||||||
)}
|
.expanded=${this._expandedFilter === "ha-filter-integrations"}
|
||||||
</ha-check-list-item>
|
.narrow=${this.narrow}
|
||||||
</ha-button-menu>
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-integrations>
|
||||||
|
<ha-filter-states
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._filters["ha-filter-states"]?.value}
|
||||||
|
.states=${this._states(this.hass.localize)}
|
||||||
|
.label=${this.hass.localize("ui.panel.config.devices.picker.state")}
|
||||||
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
|
slot="filter-pane"
|
||||||
|
.expanded=${this._expandedFilter === "ha-filter-states"}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-states>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _filterExpanded(ev) {
|
||||||
|
if (ev.detail.expanded) {
|
||||||
|
this._expandedFilter = ev.target.localName;
|
||||||
|
} else if (this._expandedFilter === ev.target.localName) {
|
||||||
|
this._expandedFilter = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterChanged(ev) {
|
||||||
|
const type = ev.target.localName;
|
||||||
|
this._filters = { ...this._filters, [type]: ev.detail };
|
||||||
|
}
|
||||||
|
|
||||||
private _batteryEntity(
|
private _batteryEntity(
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
deviceEntityLookup: DeviceEntityLookup
|
deviceEntityLookup: DeviceEntityLookup
|
||||||
@ -549,27 +576,11 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
navigate(`/config/devices/device/${deviceId}`);
|
navigate(`/config/devices/device/${deviceId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showDisabledChanged(ev: CustomEvent<RequestSelectedDetail>) {
|
|
||||||
if (ev.detail.source !== "property") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._showDisabled = ev.detail.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleSearchChange(ev: CustomEvent) {
|
private _handleSearchChange(ev: CustomEvent) {
|
||||||
this._filter = ev.detail.value;
|
this._filter = ev.detail.value;
|
||||||
history.replaceState({ filter: this._filter }, "");
|
history.replaceState({ filter: this._filter }, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearFilter() {
|
|
||||||
if (
|
|
||||||
this._activeFilters(this.entries, this._searchParms, this.hass.localize)
|
|
||||||
) {
|
|
||||||
navigate(window.location.pathname, { replace: true });
|
|
||||||
}
|
|
||||||
this._showDisabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _addDevice() {
|
private _addDevice() {
|
||||||
const { filteredConfigEntry, filteredDomains } =
|
const { filteredConfigEntry, filteredDomains } =
|
||||||
this._devicesAndFilterDomains(
|
this._devicesAndFilterDomains(
|
||||||
@ -578,8 +589,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
this.entities,
|
this.entities,
|
||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this.manifests,
|
this.manifests,
|
||||||
this._searchParms,
|
this._filters,
|
||||||
this._showDisabled,
|
|
||||||
this.hass.localize
|
this.hass.localize
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||||
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
|
||||||
import {
|
import {
|
||||||
mdiAlertCircle,
|
mdiAlertCircle,
|
||||||
mdiCancel,
|
mdiCancel,
|
||||||
mdiDelete,
|
mdiDelete,
|
||||||
mdiEyeOff,
|
mdiEyeOff,
|
||||||
mdiFilterVariant,
|
|
||||||
mdiPencilOff,
|
mdiPencilOff,
|
||||||
mdiPlus,
|
mdiPlus,
|
||||||
mdiRestoreAlert,
|
mdiRestoreAlert,
|
||||||
@ -22,7 +20,6 @@ import {
|
|||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { until } from "lit/directives/until";
|
import { until } from "lit/directives/until";
|
||||||
@ -34,7 +31,6 @@ import {
|
|||||||
PROTOCOL_INTEGRATIONS,
|
PROTOCOL_INTEGRATIONS,
|
||||||
protocolIntegrationPicked,
|
protocolIntegrationPicked,
|
||||||
} from "../../../common/integrations/protocolIntegrationPicked";
|
} from "../../../common/integrations/protocolIntegrationPicked";
|
||||||
import { navigate } from "../../../common/navigate";
|
|
||||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import type {
|
import type {
|
||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
@ -43,9 +39,14 @@ import type {
|
|||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-check-list-item";
|
import "../../../components/ha-check-list-item";
|
||||||
|
import "../../../components/ha-filter-devices";
|
||||||
|
import "../../../components/ha-filter-floor-areas";
|
||||||
|
import "../../../components/ha-filter-integrations";
|
||||||
|
import "../../../components/ha-filter-states";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
|
import "../../../components/ha-alert";
|
||||||
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
||||||
import { fullEntitiesContext } from "../../../data/context";
|
import { fullEntitiesContext } from "../../../data/context";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
@ -56,7 +57,6 @@ import {
|
|||||||
updateEntityRegistryEntry,
|
updateEntityRegistryEntry,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
import { entryIcon } from "../../../data/icons";
|
import { entryIcon } from "../../../data/icons";
|
||||||
import { domainToName } from "../../../data/integration";
|
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
@ -106,22 +106,19 @@ export class HaConfigEntities extends LitElement {
|
|||||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
_entities!: EntityRegistryEntry[];
|
_entities!: EntityRegistryEntry[];
|
||||||
|
|
||||||
@state() private _showDisabled = false;
|
|
||||||
|
|
||||||
@state() private _showHidden = false;
|
|
||||||
|
|
||||||
@state() private _showUnavailable = true;
|
|
||||||
|
|
||||||
@state() private _showReadOnly = true;
|
|
||||||
|
|
||||||
@state() private _filter: string = history.state?.filter || "";
|
@state() private _filter: string = history.state?.filter || "";
|
||||||
|
|
||||||
@state() private _numHiddenEntities = 0;
|
|
||||||
|
|
||||||
@state() private _searchParms = new URLSearchParams(window.location.search);
|
@state() private _searchParms = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
@state() private _filters: Record<
|
||||||
|
string,
|
||||||
|
{ value: string[] | undefined; items: Set<string> | undefined }
|
||||||
|
> = {};
|
||||||
|
|
||||||
@state() private _selectedEntities: string[] = [];
|
@state() private _selectedEntities: string[] = [];
|
||||||
|
|
||||||
|
@state() private _expandedFilter?: string;
|
||||||
|
|
||||||
@query("hass-tabs-subpage-data-table", true)
|
@query("hass-tabs-subpage-data-table", true)
|
||||||
private _dataTable!: HaTabsSubpageDataTable;
|
private _dataTable!: HaTabsSubpageDataTable;
|
||||||
|
|
||||||
@ -140,71 +137,41 @@ export class HaConfigEntities extends LitElement {
|
|||||||
private _locationChanged = () => {
|
private _locationChanged = () => {
|
||||||
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
||||||
this._searchParms = new URLSearchParams(window.location.search);
|
this._searchParms = new URLSearchParams(window.location.search);
|
||||||
|
this._setFiltersFromUrl();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _popState = () => {
|
private _popState = () => {
|
||||||
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
if (window.location.search.substring(1) !== this._searchParms.toString()) {
|
||||||
this._searchParms = new URLSearchParams(window.location.search);
|
this._searchParms = new URLSearchParams(window.location.search);
|
||||||
|
this._setFiltersFromUrl();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private _activeFilters = memoize(
|
private _states = memoize((localize: LocalizeFunc) => [
|
||||||
(
|
{
|
||||||
filters: URLSearchParams,
|
value: "disabled",
|
||||||
localize: LocalizeFunc,
|
label: localize("ui.panel.config.entities.picker.status.disabled"),
|
||||||
entries?: ConfigEntry[]
|
},
|
||||||
): string[] | undefined => {
|
{
|
||||||
const filterTexts: string[] = [];
|
value: "hidden",
|
||||||
filters.forEach((value, key) => {
|
label: localize("ui.panel.config.entities.picker.status.hidden"),
|
||||||
switch (key) {
|
},
|
||||||
case "config_entry": {
|
{
|
||||||
// If we are requested to show the entities for a given config entry,
|
value: "unavailable",
|
||||||
// also show the disabled ones by default.
|
label: localize("ui.panel.config.entities.picker.status.unavailable"),
|
||||||
this._showDisabled = true;
|
},
|
||||||
|
{
|
||||||
if (!entries) {
|
value: "readonly",
|
||||||
this._loadConfigEntries();
|
label: localize("ui.panel.config.entities.picker.status.readonly"),
|
||||||
break;
|
},
|
||||||
}
|
]);
|
||||||
const configEntry = entries.find(
|
|
||||||
(entry) => entry.entry_id === value
|
|
||||||
);
|
|
||||||
if (!configEntry) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const integrationName = domainToName(localize, configEntry.domain);
|
|
||||||
filterTexts.push(
|
|
||||||
`${this.hass.localize(
|
|
||||||
"ui.panel.config.integrations.integration"
|
|
||||||
)} "${integrationName}${
|
|
||||||
integrationName !== configEntry.title
|
|
||||||
? `: ${configEntry.title}`
|
|
||||||
: ""
|
|
||||||
}"`
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "domain": {
|
|
||||||
this._showDisabled = true;
|
|
||||||
filterTexts.push(
|
|
||||||
`${this.hass.localize(
|
|
||||||
"ui.panel.config.integrations.integration"
|
|
||||||
)} "${domainToName(localize, value)}"`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return filterTexts.length ? filterTexts : undefined;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
private _columns = memoize(
|
private _columns = memoize(
|
||||||
(
|
(
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
narrow,
|
narrow,
|
||||||
_language,
|
_language
|
||||||
showDisabled
|
|
||||||
): DataTableColumnContainer<EntityRow> => ({
|
): DataTableColumnContainer<EntityRow> => ({
|
||||||
icon: {
|
icon: {
|
||||||
title: "",
|
title: "",
|
||||||
@ -255,6 +222,7 @@ export class HaConfigEntities extends LitElement {
|
|||||||
title: localize("ui.panel.config.entities.picker.headers.integration"),
|
title: localize("ui.panel.config.entities.picker.headers.integration"),
|
||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
groupable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
width: "20%",
|
width: "20%",
|
||||||
},
|
},
|
||||||
@ -263,17 +231,16 @@ export class HaConfigEntities extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
hidden: narrow,
|
hidden: narrow,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
groupable: true,
|
||||||
width: "15%",
|
width: "15%",
|
||||||
},
|
},
|
||||||
disabled_by: {
|
disabled_by: {
|
||||||
title: localize("ui.panel.config.entities.picker.headers.disabled_by"),
|
title: localize("ui.panel.config.entities.picker.headers.disabled_by"),
|
||||||
sortable: true,
|
hidden: true,
|
||||||
hidden: narrow || !showDisabled,
|
|
||||||
filterable: true,
|
filterable: true,
|
||||||
width: "15%",
|
|
||||||
template: (entry) =>
|
template: (entry) =>
|
||||||
entry.disabled_by === null
|
entry.disabled_by === null
|
||||||
? "—"
|
? ""
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
`config_entry.disabled_by.${entry.disabled_by}`
|
`config_entry.disabled_by.${entry.disabled_by}`
|
||||||
),
|
),
|
||||||
@ -283,6 +250,7 @@ export class HaConfigEntities extends LitElement {
|
|||||||
type: "icon",
|
type: "icon",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
groupable: true,
|
||||||
width: "68px",
|
width: "68px",
|
||||||
template: (entry) =>
|
template: (entry) =>
|
||||||
entry.unavailable ||
|
entry.unavailable ||
|
||||||
@ -343,17 +311,24 @@ export class HaConfigEntities extends LitElement {
|
|||||||
devices: HomeAssistant["devices"],
|
devices: HomeAssistant["devices"],
|
||||||
areas: HomeAssistant["areas"],
|
areas: HomeAssistant["areas"],
|
||||||
stateEntities: StateEntity[],
|
stateEntities: StateEntity[],
|
||||||
filters: URLSearchParams,
|
filters: Record<
|
||||||
showDisabled: boolean,
|
string,
|
||||||
showUnavailable: boolean,
|
{ value: string[] | undefined; items: Set<string> | undefined }
|
||||||
showReadOnly: boolean,
|
>,
|
||||||
showHidden: boolean,
|
|
||||||
entries?: ConfigEntry[]
|
entries?: ConfigEntry[]
|
||||||
) => {
|
) => {
|
||||||
const result: EntityRow[] = [];
|
const result: EntityRow[] = [];
|
||||||
|
|
||||||
// If nothing gets filtered, this is our correct count of entities
|
const stateFilters = filters["ha-filter-states"]?.value;
|
||||||
let startLength = entities.length + stateEntities.length;
|
|
||||||
|
const showReadOnly =
|
||||||
|
!stateFilters?.length || stateFilters.includes("readonly");
|
||||||
|
const showDisabled =
|
||||||
|
!stateFilters?.length || stateFilters.includes("disabled");
|
||||||
|
const showHidden =
|
||||||
|
!stateFilters?.length || stateFilters.includes("hidden");
|
||||||
|
const showUnavailable =
|
||||||
|
!stateFilters?.length || stateFilters.includes("unavailable");
|
||||||
|
|
||||||
let filteredEntities = showReadOnly
|
let filteredEntities = showReadOnly
|
||||||
? entities.concat(stateEntities)
|
? entities.concat(stateEntities)
|
||||||
@ -362,48 +337,47 @@ export class HaConfigEntities extends LitElement {
|
|||||||
let filteredConfigEntry: ConfigEntry | undefined;
|
let filteredConfigEntry: ConfigEntry | undefined;
|
||||||
const filteredDomains = new Set<string>();
|
const filteredDomains = new Set<string>();
|
||||||
|
|
||||||
filters.forEach((value, key) => {
|
Object.entries(filters).forEach(([key, flter]) => {
|
||||||
if (key === "config_entry") {
|
if (key === "config_entry" && flter.value?.length) {
|
||||||
filteredEntities = filteredEntities.filter(
|
filteredEntities = filteredEntities.filter(
|
||||||
(entity) => entity.config_entry_id === value
|
(entity) =>
|
||||||
|
entity.config_entry_id &&
|
||||||
|
flter.value?.includes(entity.config_entry_id)
|
||||||
);
|
);
|
||||||
// If we have an active filter and `showReadOnly` is true, the length of `entities` is correct.
|
|
||||||
// If however, the read-only entities were not added before, we need to check how many would
|
|
||||||
// have matched the active filter and add that number to the count.
|
|
||||||
startLength = filteredEntities.length;
|
|
||||||
if (!showReadOnly) {
|
|
||||||
startLength += stateEntities.filter(
|
|
||||||
(entity) => entity.config_entry_id === value
|
|
||||||
).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
this._loadConfigEntries();
|
this._loadConfigEntries();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configEntry = entries.find((entry) => entry.entry_id === value);
|
const configEntries = entries.filter(
|
||||||
|
(entry) => entry.entry_id && flter.value?.includes(entry.entry_id)
|
||||||
|
);
|
||||||
|
|
||||||
if (configEntry) {
|
configEntries.forEach((configEntry) => {
|
||||||
filteredDomains.add(configEntry.domain);
|
filteredDomains.add(configEntry.domain);
|
||||||
filteredConfigEntry = configEntry;
|
});
|
||||||
|
if (configEntries.length === 1) {
|
||||||
|
filteredConfigEntry = configEntries[0];
|
||||||
}
|
}
|
||||||
}
|
} else if (key === "ha-filter-integrations" && flter.value?.length) {
|
||||||
if (key === "domain") {
|
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
this._loadConfigEntries();
|
this._loadConfigEntries();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const entryIds = entries
|
const entryIds = entries
|
||||||
.filter((entry) => entry.domain === value)
|
.filter((entry) => flter.value!.includes(entry.domain))
|
||||||
.map((entry) => entry.entry_id);
|
.map((entry) => entry.entry_id);
|
||||||
filteredEntities = filteredEntities.filter(
|
filteredEntities = filteredEntities.filter(
|
||||||
(entity) =>
|
(entity) =>
|
||||||
entity.config_entry_id &&
|
entity.config_entry_id &&
|
||||||
entryIds.includes(entity.config_entry_id)
|
entryIds.includes(entity.config_entry_id)
|
||||||
);
|
);
|
||||||
filteredDomains.add(value);
|
flter.value!.forEach((domain) => filteredDomains.add(domain));
|
||||||
startLength = filteredEntities.length;
|
} else if (flter.items) {
|
||||||
|
filteredEntities = filteredEntities.filter((entity) =>
|
||||||
|
flter.items!.has(entity.entity_id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -454,11 +428,12 @@ export class HaConfigEntities extends LitElement {
|
|||||||
? localize(
|
? localize(
|
||||||
"ui.panel.config.entities.picker.status.readonly"
|
"ui.panel.config.entities.picker.status.readonly"
|
||||||
)
|
)
|
||||||
: undefined,
|
: localize(
|
||||||
|
"ui.panel.config.entities.picker.status.available"
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this._numHiddenEntities = startLength - result.length;
|
|
||||||
return { filteredEntities: result, filteredConfigEntry, filteredDomains };
|
return { filteredEntities: result, filteredConfigEntry, filteredDomains };
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -467,11 +442,6 @@ export class HaConfigEntities extends LitElement {
|
|||||||
if (!this.hass || this._entities === undefined) {
|
if (!this.hass || this._entities === undefined) {
|
||||||
return html` <hass-loading-screen></hass-loading-screen> `;
|
return html` <hass-loading-screen></hass-loading-screen> `;
|
||||||
}
|
}
|
||||||
const activeFilters = this._activeFilters(
|
|
||||||
this._searchParms,
|
|
||||||
this.hass.localize,
|
|
||||||
this._entries
|
|
||||||
);
|
|
||||||
|
|
||||||
const { filteredEntities, filteredDomains } =
|
const { filteredEntities, filteredDomains } =
|
||||||
this._filteredEntitiesAndDomains(
|
this._filteredEntitiesAndDomains(
|
||||||
@ -480,11 +450,7 @@ export class HaConfigEntities extends LitElement {
|
|||||||
this.hass.devices,
|
this.hass.devices,
|
||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this._stateEntities,
|
this._stateEntities,
|
||||||
this._searchParms,
|
this._filters,
|
||||||
this._showDisabled,
|
|
||||||
this._showUnavailable,
|
|
||||||
this._showReadOnly,
|
|
||||||
this._showHidden,
|
|
||||||
this._entries
|
this._entries
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -506,20 +472,17 @@ export class HaConfigEntities extends LitElement {
|
|||||||
.columns=${this._columns(
|
.columns=${this._columns(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
this.narrow,
|
this.narrow,
|
||||||
this.hass.language,
|
this.hass.language
|
||||||
this._showDisabled
|
|
||||||
)}
|
)}
|
||||||
.data=${filteredEntities}
|
.data=${filteredEntities}
|
||||||
.activeFilters=${activeFilters}
|
|
||||||
.numHidden=${this._numHiddenEntities}
|
|
||||||
.hideFilterMenu=${this._selectedEntities.length > 0}
|
|
||||||
.searchLabel=${this.hass.localize(
|
.searchLabel=${this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.search"
|
"ui.panel.config.entities.picker.search"
|
||||||
)}
|
)}
|
||||||
.hiddenLabel=${this.hass.localize(
|
hasFilters
|
||||||
"ui.panel.config.entities.picker.filter.hidden_entities",
|
.filters=${Object.values(this._filters).filter(
|
||||||
{ number: this._numHiddenEntities }
|
(filter) => filter.value?.length
|
||||||
)}
|
).length}
|
||||||
|
.selected=${this._selectedEntities.length}
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
selectable
|
selectable
|
||||||
clickable
|
clickable
|
||||||
@ -534,157 +497,142 @@ export class HaConfigEntities extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
></ha-integration-overflow-menu>
|
></ha-integration-overflow-menu>
|
||||||
${this._selectedEntities.length
|
<div class="header-btns" slot="selection-bar">
|
||||||
? html`
|
${!this.narrow
|
||||||
<div
|
? html`
|
||||||
class=${classMap({
|
<mwc-button
|
||||||
"header-toolbar": this.narrow,
|
@click=${this._enableSelected}
|
||||||
"table-header": !this.narrow,
|
.disabled=${!this._selectedEntities.length}
|
||||||
})}
|
>${this.hass.localize(
|
||||||
slot="header"
|
"ui.panel.config.entities.picker.enable_selected.button"
|
||||||
>
|
)}</mwc-button
|
||||||
<p class="selected-txt">
|
>
|
||||||
${this.hass.localize(
|
<mwc-button
|
||||||
"ui.panel.config.entities.picker.selected",
|
@click=${this._disableSelected}
|
||||||
{ number: this._selectedEntities.length }
|
.disabled=${!this._selectedEntities.length}
|
||||||
)}
|
>${this.hass.localize(
|
||||||
</p>
|
"ui.panel.config.entities.picker.disable_selected.button"
|
||||||
<div class="header-btns">
|
)}</mwc-button
|
||||||
${!this.narrow
|
>
|
||||||
? html`
|
<mwc-button
|
||||||
<mwc-button @click=${this._enableSelected}
|
@click=${this._hideSelected}
|
||||||
>${this.hass.localize(
|
.disabled=${!this._selectedEntities.length}
|
||||||
"ui.panel.config.entities.picker.enable_selected.button"
|
>${this.hass.localize(
|
||||||
)}</mwc-button
|
"ui.panel.config.entities.picker.hide_selected.button"
|
||||||
>
|
)}</mwc-button
|
||||||
<mwc-button @click=${this._disableSelected}
|
>
|
||||||
>${this.hass.localize(
|
<mwc-button
|
||||||
"ui.panel.config.entities.picker.disable_selected.button"
|
@click=${this._removeSelected}
|
||||||
)}</mwc-button
|
.disabled=${!this._selectedEntities.length}
|
||||||
>
|
class="warning"
|
||||||
<mwc-button @click=${this._hideSelected}
|
>${this.hass.localize(
|
||||||
>${this.hass.localize(
|
"ui.panel.config.entities.picker.remove_selected.button"
|
||||||
"ui.panel.config.entities.picker.hide_selected.button"
|
)}</mwc-button
|
||||||
)}</mwc-button
|
>
|
||||||
>
|
`
|
||||||
<mwc-button
|
: html`
|
||||||
@click=${this._removeSelected}
|
|
||||||
class="warning"
|
|
||||||
>${this.hass.localize(
|
|
||||||
"ui.panel.config.entities.picker.remove_selected.button"
|
|
||||||
)}</mwc-button
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<ha-icon-button
|
|
||||||
id="enable-btn"
|
|
||||||
@click=${this._enableSelected}
|
|
||||||
.path=${mdiUndo}
|
|
||||||
.label=${this.hass.localize("ui.common.enable")}
|
|
||||||
></ha-icon-button>
|
|
||||||
<simple-tooltip animation-delay="0" for="enable-btn">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.entities.picker.enable_selected.button"
|
|
||||||
)}
|
|
||||||
</simple-tooltip>
|
|
||||||
<ha-icon-button
|
|
||||||
id="disable-btn"
|
|
||||||
@click=${this._disableSelected}
|
|
||||||
.path=${mdiCancel}
|
|
||||||
.label=${this.hass.localize("ui.common.disable")}
|
|
||||||
></ha-icon-button>
|
|
||||||
<simple-tooltip animation-delay="0" for="disable-btn">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.entities.picker.disable_selected.button"
|
|
||||||
)}
|
|
||||||
</simple-tooltip>
|
|
||||||
<ha-icon-button
|
|
||||||
id="hide-btn"
|
|
||||||
@click=${this._hideSelected}
|
|
||||||
.path=${mdiEyeOff}
|
|
||||||
.label=${this.hass.localize("ui.common.hide")}
|
|
||||||
></ha-icon-button>
|
|
||||||
<simple-tooltip animation-delay="0" for="hide-btn">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.entities.picker.hide_selected.button"
|
|
||||||
)}
|
|
||||||
</simple-tooltip>
|
|
||||||
<ha-icon-button
|
|
||||||
class="warning"
|
|
||||||
id="remove-btn"
|
|
||||||
@click=${this._removeSelected}
|
|
||||||
.path=${mdiDelete}
|
|
||||||
.label=${this.hass.localize("ui.common.remove")}
|
|
||||||
></ha-icon-button>
|
|
||||||
<simple-tooltip animation-delay="0" for="remove-btn">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.entities.picker.remove_selected.button"
|
|
||||||
)}
|
|
||||||
</simple-tooltip>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: html`
|
|
||||||
<ha-button-menu slot="filter-menu" multi>
|
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
id="enable-btn"
|
||||||
.label=${this.hass!.localize(
|
.disabled=${!this._selectedEntities.length}
|
||||||
"ui.panel.config.entities.picker.filter.filter"
|
@click=${this._enableSelected}
|
||||||
)}
|
.path=${mdiUndo}
|
||||||
.path=${mdiFilterVariant}
|
.label=${this.hass.localize("ui.common.enable")}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
${this.narrow && activeFilters?.length
|
<simple-tooltip animation-delay="0" for="enable-btn">
|
||||||
? html`<mwc-list-item @click=${this._clearFilter}
|
${this.hass.localize(
|
||||||
>${this.hass.localize(
|
"ui.panel.config.entities.picker.enable_selected.button"
|
||||||
"ui.components.data-table.filtering_by"
|
|
||||||
)}
|
|
||||||
${activeFilters.join(", ")}
|
|
||||||
<span class="clear"
|
|
||||||
>${this.hass.localize("ui.common.clear")}</span
|
|
||||||
></mwc-list-item
|
|
||||||
>`
|
|
||||||
: ""}
|
|
||||||
<ha-check-list-item
|
|
||||||
@request-selected=${this._showDisabledChanged}
|
|
||||||
.selected=${this._showDisabled}
|
|
||||||
left
|
|
||||||
>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.entities.picker.filter.show_disabled"
|
|
||||||
)}
|
)}
|
||||||
</ha-check-list-item>
|
</simple-tooltip>
|
||||||
<ha-check-list-item
|
<ha-icon-button
|
||||||
@request-selected=${this._showHiddenChanged}
|
id="disable-btn"
|
||||||
.selected=${this._showHidden}
|
.disabled=${!this._selectedEntities.length}
|
||||||
left
|
@click=${this._disableSelected}
|
||||||
>
|
.path=${mdiCancel}
|
||||||
${this.hass!.localize(
|
.label=${this.hass.localize("ui.common.disable")}
|
||||||
"ui.panel.config.entities.picker.filter.show_hidden"
|
></ha-icon-button>
|
||||||
|
<simple-tooltip animation-delay="0" for="disable-btn">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.entities.picker.disable_selected.button"
|
||||||
)}
|
)}
|
||||||
</ha-check-list-item>
|
</simple-tooltip>
|
||||||
<ha-check-list-item
|
<ha-icon-button
|
||||||
@request-selected=${this._showRestoredChanged}
|
id="hide-btn"
|
||||||
graphic="control"
|
.disabled=${!this._selectedEntities.length}
|
||||||
.selected=${this._showUnavailable}
|
@click=${this._hideSelected}
|
||||||
left
|
.path=${mdiEyeOff}
|
||||||
>
|
.label=${this.hass.localize("ui.common.hide")}
|
||||||
${this.hass!.localize(
|
></ha-icon-button>
|
||||||
"ui.panel.config.entities.picker.filter.show_unavailable"
|
<simple-tooltip animation-delay="0" for="hide-btn">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.entities.picker.hide_selected.button"
|
||||||
)}
|
)}
|
||||||
</ha-check-list-item>
|
</simple-tooltip>
|
||||||
<ha-check-list-item
|
<ha-icon-button
|
||||||
@request-selected=${this._showReadOnlyChanged}
|
class="warning"
|
||||||
graphic="control"
|
id="remove-btn"
|
||||||
.selected=${this._showReadOnly}
|
.disabled=${!this._selectedEntities.length}
|
||||||
left
|
@click=${this._removeSelected}
|
||||||
>
|
.path=${mdiDelete}
|
||||||
${this.hass!.localize(
|
.label=${this.hass.localize("ui.common.remove")}
|
||||||
"ui.panel.config.entities.picker.filter.show_readonly"
|
></ha-icon-button>
|
||||||
|
<simple-tooltip animation-delay="0" for="remove-btn">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.entities.picker.remove_selected.button"
|
||||||
)}
|
)}
|
||||||
</ha-check-list-item>
|
</simple-tooltip>
|
||||||
</ha-button-menu>
|
`}
|
||||||
`}
|
</div>
|
||||||
|
${this._filters.config_entry?.value?.length
|
||||||
|
? html`<ha-alert slot="filter-pane">
|
||||||
|
Filtering by config entry
|
||||||
|
${this._entries?.find(
|
||||||
|
(entry) =>
|
||||||
|
entry.entry_id === this._filters.config_entry!.value![0]
|
||||||
|
)?.title || this._filters.config_entry.value[0]}
|
||||||
|
</ha-alert>`
|
||||||
|
: nothing}
|
||||||
|
<ha-filter-floor-areas
|
||||||
|
.hass=${this.hass}
|
||||||
|
type="entity"
|
||||||
|
.value=${this._filters["ha-filter-floor-areas"]?.value}
|
||||||
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
|
slot="filter-pane"
|
||||||
|
.expanded=${this._expandedFilter === "ha-filter-floor-areas"}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-floor-areas>
|
||||||
|
<ha-filter-devices
|
||||||
|
.hass=${this.hass}
|
||||||
|
.type=${"entity"}
|
||||||
|
.value=${this._filters["ha-filter-devices"]?.value}
|
||||||
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
|
slot="filter-pane"
|
||||||
|
.expanded=${this._expandedFilter === "ha-filter-devices"}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-devices>
|
||||||
|
<ha-filter-integrations
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._filters["ha-filter-integrations"]?.value}
|
||||||
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
|
slot="filter-pane"
|
||||||
|
.expanded=${this._expandedFilter === "ha-filter-integrations"}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-integrations>
|
||||||
|
<ha-filter-states
|
||||||
|
.hass=${this.hass}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.entities.picker.headers.status"
|
||||||
|
)}
|
||||||
|
.value=${this._filters["ha-filter-states"]?.value}
|
||||||
|
.states=${this._states(this.hass.localize)}
|
||||||
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
|
slot="filter-pane"
|
||||||
|
.expanded=${this._expandedFilter === "ha-filter-states"}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-states>
|
||||||
${includeAddDeviceFab
|
${includeAddDeviceFab
|
||||||
? html`<ha-fab
|
? html`<ha-fab
|
||||||
.label=${this.hass.localize("ui.panel.config.devices.add_device")}
|
.label=${this.hass.localize("ui.panel.config.devices.add_device")}
|
||||||
@ -699,6 +647,68 @@ export class HaConfigEntities extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _filterExpanded(ev) {
|
||||||
|
if (ev.detail.expanded) {
|
||||||
|
this._expandedFilter = ev.target.localName;
|
||||||
|
} else if (this._expandedFilter === ev.target.localName) {
|
||||||
|
this._expandedFilter = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterChanged(ev) {
|
||||||
|
const type = ev.target.localName;
|
||||||
|
this._filters = { ...this._filters, [type]: ev.detail };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated() {
|
||||||
|
this._filters = {
|
||||||
|
"ha-filter-states": {
|
||||||
|
value: ["unavailable", "readonly"],
|
||||||
|
items: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
this._setFiltersFromUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setFiltersFromUrl() {
|
||||||
|
if (this._searchParms.has("domain")) {
|
||||||
|
this._filters = {
|
||||||
|
...this._filters,
|
||||||
|
"ha-filter-states": {
|
||||||
|
value: [
|
||||||
|
...(this._filters["ha-filter-states"]?.value || []),
|
||||||
|
"disabled",
|
||||||
|
],
|
||||||
|
items: undefined,
|
||||||
|
},
|
||||||
|
"ha-filter-integrations": {
|
||||||
|
value: [this._searchParms.get("domain")!],
|
||||||
|
items: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (this._searchParms.has("config_entry")) {
|
||||||
|
this._filters = {
|
||||||
|
...this._filters,
|
||||||
|
"ha-filter-states": {
|
||||||
|
value: [
|
||||||
|
...(this._filters["ha-filter-states"]?.value || []),
|
||||||
|
"disabled",
|
||||||
|
],
|
||||||
|
items: undefined,
|
||||||
|
},
|
||||||
|
config_entry: {
|
||||||
|
value: [this._searchParms.get("config_entry")!],
|
||||||
|
items: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearFilter() {
|
||||||
|
this._filters = {};
|
||||||
|
}
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues<this>): void {
|
public willUpdate(changedProps: PropertyValues<this>): void {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
const oldHass = changedProps.get("hass");
|
const oldHass = changedProps.get("hass");
|
||||||
@ -746,34 +756,6 @@ export class HaConfigEntities extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showDisabledChanged(ev: CustomEvent<RequestSelectedDetail>) {
|
|
||||||
if (ev.detail.source !== "property") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._showDisabled = ev.detail.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _showHiddenChanged(ev: CustomEvent<RequestSelectedDetail>) {
|
|
||||||
if (ev.detail.source !== "property") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._showHidden = ev.detail.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _showRestoredChanged(ev: CustomEvent<RequestSelectedDetail>) {
|
|
||||||
if (ev.detail.source !== "property") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._showUnavailable = ev.detail.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _showReadOnlyChanged(ev: CustomEvent<RequestSelectedDetail>) {
|
|
||||||
if (ev.detail.source !== "property") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._showReadOnly = ev.detail.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleSearchChange(ev: CustomEvent) {
|
private _handleSearchChange(ev: CustomEvent) {
|
||||||
this._filter = ev.detail.value;
|
this._filter = ev.detail.value;
|
||||||
history.replaceState({ filter: this._filter }, "");
|
history.replaceState({ filter: this._filter }, "");
|
||||||
@ -927,18 +909,6 @@ export class HaConfigEntities extends LitElement {
|
|||||||
this._entries = await getConfigEntries(this.hass);
|
this._entries = await getConfigEntries(this.hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearFilter() {
|
|
||||||
if (
|
|
||||||
this._activeFilters(this._searchParms, this.hass.localize, this._entries)
|
|
||||||
) {
|
|
||||||
navigate(window.location.pathname, { replace: true });
|
|
||||||
}
|
|
||||||
this._showDisabled = true;
|
|
||||||
this._showReadOnly = true;
|
|
||||||
this._showUnavailable = true;
|
|
||||||
this._showHidden = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _addDevice() {
|
private _addDevice() {
|
||||||
const { filteredConfigEntry, filteredDomains } =
|
const { filteredConfigEntry, filteredDomains } =
|
||||||
this._filteredEntitiesAndDomains(
|
this._filteredEntitiesAndDomains(
|
||||||
@ -947,11 +917,7 @@ export class HaConfigEntities extends LitElement {
|
|||||||
this.hass.devices,
|
this.hass.devices,
|
||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this._stateEntities,
|
this._stateEntities,
|
||||||
this._searchParms,
|
this._filters,
|
||||||
this._showDisabled,
|
|
||||||
this._showUnavailable,
|
|
||||||
this._showReadOnly,
|
|
||||||
this._showHidden,
|
|
||||||
this._entries
|
this._entries
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
|
@ -3,23 +3,14 @@ import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
|||||||
import {
|
import {
|
||||||
mdiCloseBoxMultiple,
|
mdiCloseBoxMultiple,
|
||||||
mdiCloseCircleOutline,
|
mdiCloseCircleOutline,
|
||||||
mdiFilterVariant,
|
|
||||||
mdiPlus,
|
mdiPlus,
|
||||||
mdiPlusBoxMultiple,
|
mdiPlusBoxMultiple,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import {
|
import { CSSResultGroup, LitElement, PropertyValues, css, html } from "lit";
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
nothing,
|
|
||||||
PropertyValues,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import memoize from "memoize-one";
|
import memoize from "memoize-one";
|
||||||
import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event";
|
import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import {
|
import {
|
||||||
EntityFilter,
|
EntityFilter,
|
||||||
@ -42,13 +33,13 @@ import {
|
|||||||
getExtendedEntityRegistryEntries,
|
getExtendedEntityRegistryEntries,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
import {
|
import {
|
||||||
exposeEntities,
|
|
||||||
ExposeEntitySettings,
|
ExposeEntitySettings,
|
||||||
|
exposeEntities,
|
||||||
voiceAssistants,
|
voiceAssistants,
|
||||||
} from "../../../data/expose";
|
} from "../../../data/expose";
|
||||||
import {
|
import {
|
||||||
fetchCloudGoogleEntities,
|
|
||||||
GoogleEntity,
|
GoogleEntity,
|
||||||
|
fetchCloudGoogleEntities,
|
||||||
} from "../../../data/google_assistant";
|
} from "../../../data/google_assistant";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-loading-screen";
|
import "../../../layouts/hass-loading-screen";
|
||||||
@ -87,8 +78,6 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
|
|
||||||
@state() private _filter: string = history.state?.filter || "";
|
@state() private _filter: string = history.state?.filter || "";
|
||||||
|
|
||||||
@state() private _numHiddenEntities = 0;
|
|
||||||
|
|
||||||
@state() private _searchParms = new URLSearchParams(window.location.search);
|
@state() private _searchParms = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
@state() private _selectedEntities: string[] = [];
|
@state() private _selectedEntities: string[] = [];
|
||||||
@ -101,23 +90,6 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
@query("hass-tabs-subpage-data-table", true)
|
@query("hass-tabs-subpage-data-table", true)
|
||||||
private _dataTable!: HaTabsSubpageDataTable;
|
private _dataTable!: HaTabsSubpageDataTable;
|
||||||
|
|
||||||
private _activeFilters = memoize(
|
|
||||||
(filters: URLSearchParams): string[] | undefined => {
|
|
||||||
const filterTexts: string[] = [];
|
|
||||||
filters.forEach((value, key) => {
|
|
||||||
switch (key) {
|
|
||||||
case "assistants": {
|
|
||||||
const assistants = value.split(",");
|
|
||||||
assistants.forEach((assistant) => {
|
|
||||||
filterTexts.push(voiceAssistants[assistant]?.name || assistant);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return filterTexts.length ? filterTexts : undefined;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
private _columns = memoize(
|
private _columns = memoize(
|
||||||
(
|
(
|
||||||
narrow: boolean,
|
narrow: boolean,
|
||||||
@ -319,9 +291,6 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// If nothing gets filtered, this is our correct count of entities
|
|
||||||
const startLength = filteredEntities.length;
|
|
||||||
|
|
||||||
let filteredAssistants: string[];
|
let filteredAssistants: string[];
|
||||||
|
|
||||||
filters.forEach((value, key) => {
|
filters.forEach((value, key) => {
|
||||||
@ -366,8 +335,6 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this._numHiddenEntities = startLength - Object.values(result).length;
|
|
||||||
|
|
||||||
if (alexaManual || googleManual) {
|
if (alexaManual || googleManual) {
|
||||||
const manFilterFuncs = this._getEntityFilterFuncs(
|
const manFilterFuncs = this._getEntityFilterFuncs(
|
||||||
(this.cloudStatus as CloudStatusLoggedIn).google_entities,
|
(this.cloudStatus as CloudStatusLoggedIn).google_entities,
|
||||||
@ -501,7 +468,6 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
if (!this.hass || !this.exposedEntities || !this._extEntities) {
|
if (!this.hass || !this.exposedEntities || !this._extEntities) {
|
||||||
return html`<hass-loading-screen></hass-loading-screen>`;
|
return html`<hass-loading-screen></hass-loading-screen>`;
|
||||||
}
|
}
|
||||||
const activeFilters = this._activeFilters(this._searchParms);
|
|
||||||
|
|
||||||
const filteredEntities = this._filteredEntities(
|
const filteredEntities = this._filteredEntities(
|
||||||
this._extEntities,
|
this._extEntities,
|
||||||
@ -529,16 +495,9 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
this.hass.localize
|
this.hass.localize
|
||||||
)}
|
)}
|
||||||
.data=${filteredEntities}
|
.data=${filteredEntities}
|
||||||
.activeFilters=${activeFilters}
|
|
||||||
.numHidden=${this._numHiddenEntities}
|
|
||||||
.hideFilterMenu=${this._selectedEntities.length > 0}
|
|
||||||
.searchLabel=${this.hass.localize(
|
.searchLabel=${this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.search"
|
"ui.panel.config.entities.picker.search"
|
||||||
)}
|
)}
|
||||||
.hiddenLabel=${this.hass.localize(
|
|
||||||
"ui.panel.config.entities.picker.filter.hidden_entities",
|
|
||||||
{ number: this._numHiddenEntities }
|
|
||||||
)}
|
|
||||||
.filter=${this._filter}
|
.filter=${this._filter}
|
||||||
selectable
|
selectable
|
||||||
.selected=${this._selectedEntities.length}
|
.selected=${this._selectedEntities.length}
|
||||||
@ -552,56 +511,48 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
>
|
>
|
||||||
${this._selectedEntities.length
|
${this._selectedEntities.length
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div class="header-btns" slot="selection-bar">
|
||||||
class=${classMap({
|
${!this.narrow
|
||||||
"header-toolbar": this.narrow,
|
? html`
|
||||||
"table-header": !this.narrow,
|
<mwc-button @click=${this._exposeSelected}
|
||||||
})}
|
>${this.hass.localize(
|
||||||
slot="header"
|
"ui.panel.config.voice_assistants.expose.expose"
|
||||||
>
|
)}</mwc-button
|
||||||
<div class="header-btns">
|
>
|
||||||
${!this.narrow
|
<mwc-button @click=${this._unexposeSelected}
|
||||||
? html`
|
>${this.hass.localize(
|
||||||
<mwc-button @click=${this._exposeSelected}
|
"ui.panel.config.voice_assistants.expose.unexpose"
|
||||||
>${this.hass.localize(
|
)}</mwc-button
|
||||||
"ui.panel.config.voice_assistants.expose.expose"
|
>
|
||||||
)}</mwc-button
|
`
|
||||||
>
|
: html`
|
||||||
<mwc-button @click=${this._unexposeSelected}
|
<ha-icon-button
|
||||||
>${this.hass.localize(
|
id="enable-btn"
|
||||||
"ui.panel.config.voice_assistants.expose.unexpose"
|
@click=${this._exposeSelected}
|
||||||
)}</mwc-button
|
.path=${mdiPlusBoxMultiple}
|
||||||
>
|
.label=${this.hass.localize(
|
||||||
`
|
"ui.panel.config.voice_assistants.expose.expose"
|
||||||
: html`
|
)}
|
||||||
<ha-icon-button
|
></ha-icon-button>
|
||||||
id="enable-btn"
|
<simple-tooltip animation-delay="0" for="enable-btn">
|
||||||
@click=${this._exposeSelected}
|
${this.hass.localize(
|
||||||
.path=${mdiPlusBoxMultiple}
|
"ui.panel.config.voice_assistants.expose.expose"
|
||||||
.label=${this.hass.localize(
|
)}
|
||||||
"ui.panel.config.voice_assistants.expose.expose"
|
</simple-tooltip>
|
||||||
)}
|
<ha-icon-button
|
||||||
></ha-icon-button>
|
id="disable-btn"
|
||||||
<simple-tooltip animation-delay="0" for="enable-btn">
|
@click=${this._unexposeSelected}
|
||||||
${this.hass.localize(
|
.path=${mdiCloseBoxMultiple}
|
||||||
"ui.panel.config.voice_assistants.expose.expose"
|
.label=${this.hass.localize(
|
||||||
)}
|
"ui.panel.config.voice_assistants.expose.unexpose"
|
||||||
</simple-tooltip>
|
)}
|
||||||
<ha-icon-button
|
></ha-icon-button>
|
||||||
id="disable-btn"
|
<simple-tooltip animation-delay="0" for="disable-btn">
|
||||||
@click=${this._unexposeSelected}
|
${this.hass.localize(
|
||||||
.path=${mdiCloseBoxMultiple}
|
"ui.panel.config.voice_assistants.expose.unexpose"
|
||||||
.label=${this.hass.localize(
|
)}
|
||||||
"ui.panel.config.voice_assistants.expose.unexpose"
|
</simple-tooltip>
|
||||||
)}
|
`}
|
||||||
></ha-icon-button>
|
|
||||||
<simple-tooltip animation-delay="0" for="disable-btn">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.voice_assistants.expose.unexpose"
|
|
||||||
)}
|
|
||||||
</simple-tooltip>
|
|
||||||
`}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@ -615,26 +566,6 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</ha-fab>
|
</ha-fab>
|
||||||
${this.narrow && activeFilters?.length
|
|
||||||
? html`
|
|
||||||
<ha-button-menu slot="filter-menu" multi>
|
|
||||||
<ha-icon-button
|
|
||||||
slot="trigger"
|
|
||||||
.label=${this.hass!.localize(
|
|
||||||
"ui.panel.config.devices.picker.filter.filter"
|
|
||||||
)}
|
|
||||||
.path=${mdiFilterVariant}
|
|
||||||
></ha-icon-button>
|
|
||||||
<mwc-list-item @click=${this._clearFilter}>
|
|
||||||
${this.hass.localize("ui.components.data-table.filtering_by")}
|
|
||||||
${activeFilters.join(", ")}
|
|
||||||
<span class="clear">
|
|
||||||
${this.hass.localize("ui.common.clear")}
|
|
||||||
</span>
|
|
||||||
</mwc-list-item>
|
|
||||||
</ha-button-menu>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -759,9 +690,7 @@ export class VoiceAssistantsExpose extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _clearFilter() {
|
private _clearFilter() {
|
||||||
if (this._activeFilters(this._searchParms)) {
|
navigate(window.location.pathname, { replace: true });
|
||||||
navigate(window.location.pathname, { replace: true });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@ -3994,12 +3994,7 @@
|
|||||||
"confirm_delete_integration": "Are you sure you want to remove this device from {integration}?",
|
"confirm_delete_integration": "Are you sure you want to remove this device from {integration}?",
|
||||||
"picker": {
|
"picker": {
|
||||||
"search": "Search devices",
|
"search": "Search devices",
|
||||||
"filter": {
|
"state": "State"
|
||||||
"filter": "Filter",
|
|
||||||
"show_disabled": "Show disabled devices",
|
|
||||||
"hidden_devices": "{number} {number, plural,\n one {device}\n other {devices}\n} not shown",
|
|
||||||
"show_all": "Show all"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entities": {
|
"entities": {
|
||||||
@ -4011,15 +4006,6 @@
|
|||||||
"introduction2": "Use the entity registry to override the name, change the entity ID or remove the entry from Home Assistant.",
|
"introduction2": "Use the entity registry to override the name, change the entity ID or remove the entry from Home Assistant.",
|
||||||
"search": "Search entities",
|
"search": "Search entities",
|
||||||
"unnamed_entity": "Unnamed entity",
|
"unnamed_entity": "Unnamed entity",
|
||||||
"filter": {
|
|
||||||
"filter": "Filter",
|
|
||||||
"show_hidden": "Show hidden entities",
|
|
||||||
"show_disabled": "Show disabled entities",
|
|
||||||
"show_unavailable": "Show unavailable entities",
|
|
||||||
"show_readonly": "Show read-only entities",
|
|
||||||
"hidden_entities": "{number} {number, plural,\n one {entity}\n other {entities}\n} not shown",
|
|
||||||
"show_all": "Show all"
|
|
||||||
},
|
|
||||||
"status": {
|
"status": {
|
||||||
"restored": "Restored",
|
"restored": "Restored",
|
||||||
"available": "Available",
|
"available": "Available",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user