mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-21 00:06:35 +00:00
include entities not in entity registry in config entities (#4867)
* include entities not in entity registry in config entities * Update ha-data-table.ts * Comments * Update ha-device-entities-card.ts * Comments
This commit is contained in:
parent
8f9a6bd544
commit
49b0c8d549
@ -80,6 +80,7 @@ export interface DataTableColumnData extends DataTableSortColumnData {
|
||||
|
||||
export interface DataTableRowData {
|
||||
[key: string]: any;
|
||||
selectable?: boolean;
|
||||
}
|
||||
|
||||
@customElement("ha-data-table")
|
||||
@ -249,6 +250,7 @@ export class HaDataTable extends BaseElement {
|
||||
data-row-id="${row[this.id]}"
|
||||
@click=${this._handleRowClick}
|
||||
class="mdc-data-table__row"
|
||||
.selectable=${row.selectable !== false}
|
||||
>
|
||||
${this.selectable
|
||||
? html`
|
||||
@ -258,6 +260,7 @@ export class HaDataTable extends BaseElement {
|
||||
<ha-checkbox
|
||||
class="mdc-data-table__row-checkbox"
|
||||
@change=${this._handleRowCheckboxChange}
|
||||
.disabled=${row.selectable === false}
|
||||
.checked=${this._checkedRows.includes(
|
||||
String(row[this.id])
|
||||
)}
|
||||
@ -298,9 +301,12 @@ export class HaDataTable extends BaseElement {
|
||||
protected createAdapter(): MDCDataTableAdapter {
|
||||
return {
|
||||
addClassAtRowIndex: (rowIndex: number, cssClasses: string) => {
|
||||
if (!(this.rowElements[rowIndex] as any).selectable) {
|
||||
return;
|
||||
}
|
||||
this.rowElements[rowIndex].classList.add(cssClasses);
|
||||
},
|
||||
getRowCount: () => this.data.length,
|
||||
getRowCount: () => this.rowElements.length,
|
||||
getRowElements: () => this.rowElements,
|
||||
getRowIdAtIndex: (rowIndex: number) => this._getRowIdAtIndex(rowIndex),
|
||||
getRowIndexByChildElement: (el: Element) =>
|
||||
@ -309,7 +315,7 @@ export class HaDataTable extends BaseElement {
|
||||
isCheckboxAtRowIndexChecked: (rowIndex: number) =>
|
||||
this._checkedRows.includes(this._getRowIdAtIndex(rowIndex)),
|
||||
isHeaderRowCheckboxChecked: () => this._headerChecked,
|
||||
isRowsSelectable: () => true,
|
||||
isRowsSelectable: () => this.selectable,
|
||||
notifyRowSelectionChanged: () => undefined,
|
||||
notifySelectedAll: () => undefined,
|
||||
notifyUnselectedAll: () => undefined,
|
||||
@ -332,6 +338,9 @@ export class HaDataTable extends BaseElement {
|
||||
this._headerIndeterminate = indeterminate;
|
||||
},
|
||||
setRowCheckboxCheckedAtIndex: (rowIndex: number, checked: boolean) => {
|
||||
if (!(this.rowElements[rowIndex] as any).selectable) {
|
||||
return;
|
||||
}
|
||||
this._setRowChecked(this._getRowIdAtIndex(rowIndex), checked);
|
||||
},
|
||||
};
|
||||
@ -516,6 +525,7 @@ export class HaDataTable extends BaseElement {
|
||||
padding-left: 16px;
|
||||
/* @noflip */
|
||||
padding-right: 0;
|
||||
width: 40px;
|
||||
}
|
||||
[dir="rtl"] .mdc-data-table__header-cell--checkbox,
|
||||
.mdc-data-table__header-cell--checkbox[dir="rtl"],
|
||||
@ -558,6 +568,7 @@ export class HaDataTable extends BaseElement {
|
||||
.mdc-data-table__cell--icon {
|
||||
color: var(--secondary-text-color);
|
||||
text-align: center;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.mdc-data-table__header-cell {
|
||||
|
@ -68,6 +68,13 @@ export class HaRelatedItems extends SubscribeMixin(LitElement) {
|
||||
if (!this._related) {
|
||||
return html``;
|
||||
}
|
||||
if (Object.keys(this._related).length === 0) {
|
||||
return html`
|
||||
<p>
|
||||
${this.hass.localize("ui.components.related-items.no_related_found")}
|
||||
</p>
|
||||
`;
|
||||
}
|
||||
return html`
|
||||
${this._related.config_entry && this._entries
|
||||
? this._related.config_entry.map((relatedConfigEntryId) => {
|
||||
|
@ -20,6 +20,7 @@ import "../../common/search/search-input";
|
||||
import { styleMap } from "lit-html/directives/style-map";
|
||||
import { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||
import { configFlowContentStyles } from "./styles";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
|
||||
interface HandlerObj {
|
||||
name: string;
|
||||
@ -69,7 +70,10 @@ class StepFlowPickHandler extends LitElement {
|
||||
.filter=${this.filter}
|
||||
@value-changed=${this._filterChanged}
|
||||
></search-input>
|
||||
<div style=${styleMap({ width: `${this._width}px` })}>
|
||||
<div
|
||||
style=${styleMap({ width: `${this._width}px` })}
|
||||
class=${classMap({ advanced: Boolean(this.showAdvanced) })}
|
||||
>
|
||||
${handlers.map(
|
||||
(handler: HandlerObj) =>
|
||||
html`
|
||||
@ -143,6 +147,14 @@ class StepFlowPickHandler extends LitElement {
|
||||
overflow: auto;
|
||||
max-height: 600px;
|
||||
}
|
||||
@media all and (max-height: 1px) {
|
||||
div {
|
||||
max-height: calc(100vh - 205px);
|
||||
}
|
||||
div.advanced {
|
||||
max-height: calc(100vh - 300px);
|
||||
}
|
||||
}
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -152,6 +152,7 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
const entry = (ev.currentTarget! as any).entry;
|
||||
showEntityRegistryDetailDialog(this, {
|
||||
entry,
|
||||
entity_id: entry.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,8 @@ export class DialogEntityRegistryDetail extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
const entry = this._params.entry;
|
||||
const stateObj: HassEntity | undefined = this.hass.states[entry.entity_id];
|
||||
const entityId = this._params.entity_id;
|
||||
const stateObj: HassEntity | undefined = this.hass.states[entityId];
|
||||
|
||||
return html`
|
||||
<ha-paper-dialog
|
||||
@ -68,9 +69,7 @@ export class DialogEntityRegistryDetail extends LitElement {
|
||||
dialog-dismiss
|
||||
></paper-icon-button>
|
||||
<div class="main-title" main-title>
|
||||
${stateObj
|
||||
? computeStateName(stateObj)
|
||||
: entry.name || entry.entity_id}
|
||||
${stateObj ? computeStateName(stateObj) : entry?.name || entityId}
|
||||
</div>
|
||||
${stateObj
|
||||
? html`
|
||||
@ -99,20 +98,28 @@ export class DialogEntityRegistryDetail extends LitElement {
|
||||
</paper-tabs>
|
||||
${cache(
|
||||
this._curTab === "tab-settings"
|
||||
? html`
|
||||
<entity-registry-settings
|
||||
.hass=${this.hass}
|
||||
.entry=${entry}
|
||||
.dialogElement=${this._dialog}
|
||||
@close-dialog=${this._closeDialog}
|
||||
></entity-registry-settings>
|
||||
`
|
||||
? entry
|
||||
? html`
|
||||
<entity-registry-settings
|
||||
.hass=${this.hass}
|
||||
.entry=${entry}
|
||||
.dialogElement=${this._dialog}
|
||||
@close-dialog=${this._closeDialog}
|
||||
></entity-registry-settings>
|
||||
`
|
||||
: html`
|
||||
<paper-dialog-scrollable>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.no_unique_id"
|
||||
)}
|
||||
</paper-dialog-scrollable>
|
||||
`
|
||||
: this._curTab === "tab-related"
|
||||
? html`
|
||||
<paper-dialog-scrollable>
|
||||
<ha-related-items
|
||||
.hass=${this.hass}
|
||||
.itemId=${entry.entity_id}
|
||||
.itemId=${entityId}
|
||||
itemType="entity"
|
||||
@close-dialog=${this._closeDialog}
|
||||
></ha-related-items>
|
||||
@ -139,7 +146,7 @@ export class DialogEntityRegistryDetail extends LitElement {
|
||||
|
||||
private _openMoreInfo(): void {
|
||||
fireEvent(this, "hass-more-info", {
|
||||
entityId: this._params!.entry.entity_id,
|
||||
entityId: this._params!.entity_id,
|
||||
});
|
||||
this._params = undefined;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-tooltip/paper-tooltip";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { UnsubscribeFunc, HassEntities } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
@ -22,7 +22,6 @@ import { stateIcon } from "../../../common/entity/state_icon";
|
||||
import {
|
||||
DataTableColumnContainer,
|
||||
DataTableColumnData,
|
||||
HaDataTable,
|
||||
RowClickedEvent,
|
||||
SelectionChangedEvent,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
@ -47,6 +46,20 @@ import {
|
||||
} from "./show-dialog-entity-registry-detail";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
// tslint:disable-next-line: no-duplicate-imports
|
||||
import { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table";
|
||||
|
||||
export interface StateEntity extends EntityRegistryEntry {
|
||||
readonly?: boolean;
|
||||
selectable?: boolean;
|
||||
}
|
||||
|
||||
export interface EntityRow extends StateEntity {
|
||||
icon: string;
|
||||
unavailable: boolean;
|
||||
status: string;
|
||||
}
|
||||
|
||||
@customElement("ha-config-entities")
|
||||
export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
@ -57,9 +70,11 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
@property() private _entities?: EntityRegistryEntry[];
|
||||
@property() private _showDisabled = false;
|
||||
@property() private _showUnavailable = true;
|
||||
@property() private _showReadOnly = true;
|
||||
@property() private _filter = "";
|
||||
@property() private _selectedEntities: string[] = [];
|
||||
@query("ha-data-table") private _dataTable!: HaDataTable;
|
||||
@query("hass-tabs-subpage-data-table")
|
||||
private _dataTable!: HaTabsSubpageDataTable;
|
||||
private getDialog?: () => DialogEntityRegistryDetail | undefined;
|
||||
|
||||
private _columns = memoize(
|
||||
@ -90,7 +105,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
template: (_status, entity: any) =>
|
||||
entity.unavailable || entity.disabled_by
|
||||
entity.unavailable || entity.disabled_by || entity.readonly
|
||||
? html`
|
||||
<div
|
||||
tabindex="0"
|
||||
@ -102,15 +117,21 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
})}
|
||||
.icon=${entity.unavailable
|
||||
? "hass:alert-circle"
|
||||
: "hass:cancel"}
|
||||
: entity.disabled_by
|
||||
? "hass:cancel"
|
||||
: "hass:pencil-off"}
|
||||
></ha-icon>
|
||||
<paper-tooltip position="left">
|
||||
${entity.unavailable
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.unavailable"
|
||||
)
|
||||
: this.hass.localize(
|
||||
: entity.disabled_by
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.disabled"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.entities.picker.status.readonly"
|
||||
)}
|
||||
</paper-tooltip>
|
||||
</div>
|
||||
@ -156,21 +177,43 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
private _filteredEntities = memoize(
|
||||
(
|
||||
entities: EntityRegistryEntry[],
|
||||
states: HassEntities,
|
||||
showDisabled: boolean,
|
||||
showUnavailable: boolean
|
||||
) => {
|
||||
showUnavailable: boolean,
|
||||
showReadOnly: boolean
|
||||
): EntityRow[] => {
|
||||
const stateEntities: StateEntity[] = [];
|
||||
if (showReadOnly) {
|
||||
const regEntityIds = new Set(
|
||||
entities.map((entity) => entity.entity_id)
|
||||
);
|
||||
for (const entityId of Object.keys(states)) {
|
||||
if (regEntityIds.has(entityId)) {
|
||||
continue;
|
||||
}
|
||||
stateEntities.push({
|
||||
name: computeStateName(states[entityId]),
|
||||
entity_id: entityId,
|
||||
platform: computeDomain(entityId),
|
||||
disabled_by: null,
|
||||
readonly: true,
|
||||
selectable: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!showDisabled) {
|
||||
entities = entities.filter((entity) => !Boolean(entity.disabled_by));
|
||||
}
|
||||
|
||||
return entities.reduce((result, entry) => {
|
||||
const state = this.hass!.states[entry.entity_id];
|
||||
const result: EntityRow[] = [];
|
||||
|
||||
const unavailable =
|
||||
state && (state.state === "unavailable" || state.attributes.restored); // if there is not state it is disabled
|
||||
for (const entry of entities.concat(stateEntities)) {
|
||||
const state = states[entry.entity_id];
|
||||
const unavailable = state?.state === "unavailable";
|
||||
|
||||
if (!showUnavailable && unavailable) {
|
||||
return result;
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push({
|
||||
@ -192,8 +235,9 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
)
|
||||
: this.hass.localize("ui.panel.config.entities.picker.status.ok"),
|
||||
});
|
||||
return result;
|
||||
}, [] as any);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
);
|
||||
|
||||
@ -322,6 +366,15 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
"ui.panel.config.entities.picker.filter.show_unavailable"
|
||||
)}
|
||||
</paper-icon-item>
|
||||
<paper-icon-item @tap="${this._showReadOnlyChanged}">
|
||||
<paper-checkbox
|
||||
.checked=${this._showReadOnly}
|
||||
slot="item-icon"
|
||||
></paper-checkbox>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_readonly"
|
||||
)}
|
||||
</paper-icon-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
`;
|
||||
@ -336,8 +389,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
.columns=${this._columns(this.narrow, this.hass.language)}
|
||||
.data=${this._filteredEntities(
|
||||
this._entities,
|
||||
this.hass.states,
|
||||
this._showDisabled,
|
||||
this._showUnavailable
|
||||
this._showUnavailable,
|
||||
this._showReadOnly
|
||||
)}
|
||||
.filter=${this._filter}
|
||||
selectable
|
||||
@ -369,6 +424,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
this._showUnavailable = !this._showUnavailable;
|
||||
}
|
||||
|
||||
private _showReadOnlyChanged() {
|
||||
this._showReadOnly = !this._showReadOnly;
|
||||
}
|
||||
|
||||
private _handleSearchChange(ev: CustomEvent) {
|
||||
this._filter = ev.detail.value;
|
||||
}
|
||||
@ -461,15 +520,13 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
private _openEditEntry(ev: CustomEvent): void {
|
||||
const entryId = (ev.detail as RowClickedEvent).id;
|
||||
const entityId = (ev.detail as RowClickedEvent).id;
|
||||
const entry = this._entities!.find(
|
||||
(entity) => entity.entity_id === entryId
|
||||
(entity) => entity.entity_id === entityId
|
||||
);
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
this.getDialog = showEntityRegistryDetailDialog(this, {
|
||||
entry,
|
||||
entity_id: entityId,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@ import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||
import { DialogEntityRegistryDetail } from "./dialog-entity-registry-detail";
|
||||
|
||||
export interface EntityRegistryDetailDialogParams {
|
||||
entry: EntityRegistryEntry;
|
||||
entry?: EntityRegistryEntry;
|
||||
entity_id: string;
|
||||
}
|
||||
|
||||
export const loadEntityRegistryDetailDialog = () =>
|
||||
@ -21,12 +22,12 @@ const getDialog = () => {
|
||||
|
||||
export const showEntityRegistryDetailDialog = (
|
||||
element: HTMLElement,
|
||||
systemLogDetailParams: EntityRegistryDetailDialogParams
|
||||
entityDetailParams: EntityRegistryDetailDialogParams
|
||||
): (() => DialogEntityRegistryDetail | undefined) => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-entity-registry-detail",
|
||||
dialogImport: loadEntityRegistryDetailDialog,
|
||||
dialogParams: systemLogDetailParams,
|
||||
dialogParams: entityDetailParams,
|
||||
});
|
||||
return getDialog;
|
||||
};
|
||||
|
@ -572,6 +572,7 @@
|
||||
"service": "Service"
|
||||
},
|
||||
"related-items": {
|
||||
"no_related_found": "No related items found.",
|
||||
"integration": "Integration",
|
||||
"device": "Device",
|
||||
"area": "Area",
|
||||
@ -640,6 +641,7 @@
|
||||
"control": "Control",
|
||||
"related": "Related",
|
||||
"dismiss": "Dismiss",
|
||||
"no_unique_id": "This entity does not have a unique ID, therefore it's settings can not be managed from the UI.",
|
||||
"editor": {
|
||||
"name": "Name Override",
|
||||
"entity_id": "Entity ID",
|
||||
@ -1345,11 +1347,13 @@
|
||||
"filter": {
|
||||
"filter": "Filter",
|
||||
"show_disabled": "Show disabled entities",
|
||||
"show_unavailable": "Show unavailable entities"
|
||||
"show_unavailable": "Show unavailable entities",
|
||||
"show_readonly": "Show read-only entities"
|
||||
},
|
||||
"status": {
|
||||
"unavailable": "Unavailable",
|
||||
"disabled": "Disabled",
|
||||
"readonly": "Read-only",
|
||||
"ok": "Ok"
|
||||
},
|
||||
"headers": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user