mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-02 05:57:54 +00:00
Add entity registry functions to device page (#3843)
* Add entity registry functions to device page * Move update/remove logic to dialog * Hide disabled entities by default
This commit is contained in:
parent
76d3218130
commit
b927a3ef29
@ -123,6 +123,7 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<template is="dom-if" if="[[!hideEntities]]">
|
||||||
<div class="device-entities">
|
<div class="device-entities">
|
||||||
<template
|
<template
|
||||||
is="dom-repeat"
|
is="dom-repeat"
|
||||||
@ -141,6 +142,7 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
</paper-icon-item>
|
</paper-icon-item>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -157,6 +159,7 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
},
|
},
|
||||||
hideSettings: { type: Boolean, value: false },
|
hideSettings: { type: Boolean, value: false },
|
||||||
|
hideEntities: { type: Boolean, value: false },
|
||||||
_childDevices: {
|
_childDevices: {
|
||||||
type: Array,
|
type: Array,
|
||||||
computed: "_computeChildDevices(device, devices)",
|
computed: "_computeChildDevices(device, devices)",
|
||||||
|
@ -0,0 +1,171 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
|
||||||
|
import { compare } from "../../../../common/string/compare";
|
||||||
|
import "../../../../components/entity/state-badge";
|
||||||
|
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import "../../../../components/ha-icon";
|
||||||
|
import "../../../../components/ha-switch";
|
||||||
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
|
import { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||||
|
import { showEntityRegistryDetailDialog } from "../../entity_registry/show-dialog-entity-registry-detail";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
|
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { HaSwitch } from "../../../../components/ha-switch";
|
||||||
|
|
||||||
|
@customElement("ha-device-entities-card")
|
||||||
|
export class HaDeviceEntitiesCard extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public deviceId!: string;
|
||||||
|
@property() public entities!: EntityRegistryEntry[];
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
@property() private _showDisabled = false;
|
||||||
|
|
||||||
|
private _entities = memoizeOne(
|
||||||
|
(
|
||||||
|
deviceId: string,
|
||||||
|
entities: EntityRegistryEntry[]
|
||||||
|
): EntityRegistryEntry[] =>
|
||||||
|
entities
|
||||||
|
.filter((entity) => entity.device_id === deviceId)
|
||||||
|
.sort((ent1, ent2) =>
|
||||||
|
compare(
|
||||||
|
this._computeEntityName(ent1) || `zzz${ent1.entity_id}`,
|
||||||
|
this._computeEntityName(ent2) || `zzz${ent2.entity_id}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const entities = this._entities(this.deviceId, this.entities);
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<paper-item>
|
||||||
|
<ha-switch
|
||||||
|
?checked=${this._showDisabled}
|
||||||
|
@change=${this._showDisabledChanged}
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.entity_registry.picker.show_disabled"
|
||||||
|
)}
|
||||||
|
</ha-switch>
|
||||||
|
</paper-item>
|
||||||
|
${entities.length
|
||||||
|
? entities.map((entry: EntityRegistryEntry) => {
|
||||||
|
if (!this._showDisabled && entry.disabled_by) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const stateObj = this.hass.states[entry.entity_id];
|
||||||
|
return html`
|
||||||
|
<paper-icon-item
|
||||||
|
.entry=${entry}
|
||||||
|
class=${classMap({ "disabled-entry": !!entry.disabled_by })}
|
||||||
|
>
|
||||||
|
${stateObj
|
||||||
|
? html`
|
||||||
|
<state-badge
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
slot="item-icon"
|
||||||
|
></state-badge>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-icon
|
||||||
|
slot="item-icon"
|
||||||
|
.icon=${domainIcon(computeDomain(entry.entity_id))}
|
||||||
|
></ha-icon>
|
||||||
|
`}
|
||||||
|
<paper-item-body two-line>
|
||||||
|
<div class="name">${this._computeEntityName(entry)}</div>
|
||||||
|
<div class="secondary entity-id">${entry.entity_id}</div>
|
||||||
|
</paper-item-body>
|
||||||
|
<div class="buttons">
|
||||||
|
${stateObj
|
||||||
|
? html`
|
||||||
|
<paper-icon-button
|
||||||
|
@click=${this._openMoreInfo}
|
||||||
|
icon="hass:open-in-new"
|
||||||
|
></paper-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<paper-icon-button
|
||||||
|
@click=${this._openEditEntry}
|
||||||
|
icon="hass:settings"
|
||||||
|
></paper-icon-button>
|
||||||
|
</div>
|
||||||
|
</paper-icon-item>
|
||||||
|
`;
|
||||||
|
})
|
||||||
|
: html`
|
||||||
|
<div class="config-entry-row">
|
||||||
|
<paper-item-body two-line>
|
||||||
|
<div>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.entities.none"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</paper-item-body>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _showDisabledChanged(ev: Event) {
|
||||||
|
this._showDisabled = (ev.target as HaSwitch).checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openEditEntry(ev: MouseEvent): void {
|
||||||
|
const entry = (ev.currentTarget! as any).closest("paper-icon-item").entry;
|
||||||
|
showEntityRegistryDetailDialog(this, {
|
||||||
|
entry,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openMoreInfo(ev: MouseEvent) {
|
||||||
|
const entry = (ev.currentTarget! as any).closest("paper-icon-item").entry;
|
||||||
|
fireEvent(this, "hass-more-info", { entityId: entry.entity_id });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeEntityName(entity) {
|
||||||
|
if (entity.name) {
|
||||||
|
return entity.name;
|
||||||
|
}
|
||||||
|
const state = this.hass.states[entity.entity_id];
|
||||||
|
return state ? computeStateName(state) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
ha-icon {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
.entity-id {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.buttons {
|
||||||
|
text-align: right;
|
||||||
|
margin: 0 0 0 8px;
|
||||||
|
}
|
||||||
|
.disabled-entry {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
@ -11,11 +11,12 @@ import memoizeOne from "memoize-one";
|
|||||||
|
|
||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
import "../../../layouts/hass-error-screen";
|
import "../../../layouts/hass-error-screen";
|
||||||
|
import "../ha-config-section";
|
||||||
|
|
||||||
import "./device-detail/ha-device-card";
|
|
||||||
import "./device-detail/ha-device-triggers-card";
|
import "./device-detail/ha-device-triggers-card";
|
||||||
import "./device-detail/ha-device-conditions-card";
|
import "./device-detail/ha-device-conditions-card";
|
||||||
import "./device-detail/ha-device-actions-card";
|
import "./device-detail/ha-device-actions-card";
|
||||||
|
import "./device-detail/ha-device-entities-card";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { ConfigEntry } from "../../../data/config_entries";
|
import { ConfigEntry } from "../../../data/config_entries";
|
||||||
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||||
@ -37,6 +38,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
@property() public entities!: EntityRegistryEntry[];
|
@property() public entities!: EntityRegistryEntry[];
|
||||||
@property() public areas!: AreaRegistryEntry[];
|
@property() public areas!: AreaRegistryEntry[];
|
||||||
@property() public deviceId!: string;
|
@property() public deviceId!: string;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
private _device = memoizeOne(
|
private _device = memoizeOne(
|
||||||
(
|
(
|
||||||
@ -67,7 +69,11 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
icon="hass:settings"
|
icon="hass:settings"
|
||||||
@click=${this._showSettings}
|
@click=${this._showSettings}
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
<div class="content">
|
<ha-config-section .isWide=${!this.narrow}>
|
||||||
|
<span slot="header">Device info</span>
|
||||||
|
<span slot="introduction">
|
||||||
|
Here are all the details of your device.
|
||||||
|
</span>
|
||||||
<ha-device-card
|
<ha-device-card
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.areas=${this.areas}
|
.areas=${this.areas}
|
||||||
@ -75,7 +81,18 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
.device=${device}
|
.device=${device}
|
||||||
.entities=${this.entities}
|
.entities=${this.entities}
|
||||||
hide-settings
|
hide-settings
|
||||||
|
hide-entities
|
||||||
></ha-device-card>
|
></ha-device-card>
|
||||||
|
|
||||||
|
<div class="header">Entities</div>
|
||||||
|
<ha-device-entities-card
|
||||||
|
.hass=${this.hass}
|
||||||
|
.deviceId=${this.deviceId}
|
||||||
|
.entities=${this.entities}
|
||||||
|
>
|
||||||
|
</ha-device-entities-card>
|
||||||
|
|
||||||
|
<div class="header">Automations</div>
|
||||||
<ha-device-triggers-card
|
<ha-device-triggers-card
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.deviceId=${this.deviceId}
|
.deviceId=${this.deviceId}
|
||||||
@ -88,7 +105,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.deviceId=${this.deviceId}
|
.deviceId=${this.deviceId}
|
||||||
></ha-device-actions-card>
|
></ha-device-actions-card>
|
||||||
</div>
|
</ha-config-section>
|
||||||
</hass-subpage>
|
</hass-subpage>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -104,12 +121,20 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
.content {
|
.header {
|
||||||
padding: 16px;
|
font-family: var(--paper-font-display1_-_font-family);
|
||||||
|
-webkit-font-smoothing: var(
|
||||||
|
--paper-font-display1_-_-webkit-font-smoothing
|
||||||
|
);
|
||||||
|
font-size: var(--paper-font-display1_-_font-size);
|
||||||
|
font-weight: var(--paper-font-display1_-_font-weight);
|
||||||
|
letter-spacing: var(--paper-font-display1_-_letter-spacing);
|
||||||
|
line-height: var(--paper-font-display1_-_line-height);
|
||||||
|
opacity: var(--dark-primary-opacity);
|
||||||
}
|
}
|
||||||
.content > * {
|
|
||||||
display: block;
|
ha-config-section *:last-child {
|
||||||
margin-bottom: 16px;
|
padding-bottom: 24px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,15 @@ import { HaSwitch } from "../../../components/ha-switch";
|
|||||||
|
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
|
import {
|
||||||
|
updateEntityRegistryEntry,
|
||||||
|
removeEntityRegistryEntry,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
|
|
||||||
class DialogEntityRegistryDetail extends LitElement {
|
class DialogEntityRegistryDetail extends LitElement {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
@property() private _name!: string;
|
@property() private _name!: string;
|
||||||
|
@property() private _platform!: string;
|
||||||
@property() private _entityId!: string;
|
@property() private _entityId!: string;
|
||||||
@property() private _disabledBy!: string | null;
|
@property() private _disabledBy!: string | null;
|
||||||
@property() private _error?: string;
|
@property() private _error?: string;
|
||||||
@ -38,6 +43,7 @@ class DialogEntityRegistryDetail extends LitElement {
|
|||||||
this._params = params;
|
this._params = params;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._name = this._params.entry.name || "";
|
this._name = this._params.entry.name || "";
|
||||||
|
this._platform = this._params.entry.platform;
|
||||||
this._entityId = this._params.entry.entity_id;
|
this._entityId = this._params.entry.entity_id;
|
||||||
this._disabledBy = this._params.entry.disabled_by;
|
this._disabledBy = this._params.entry.disabled_by;
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
@ -164,7 +170,7 @@ class DialogEntityRegistryDetail extends LitElement {
|
|||||||
private async _updateEntry(): Promise<void> {
|
private async _updateEntry(): Promise<void> {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
await this._params!.updateEntry({
|
await updateEntityRegistryEntry(this.hass!, this._entityId, {
|
||||||
name: this._name.trim() || null,
|
name: this._name.trim() || null,
|
||||||
disabled_by: this._disabledBy,
|
disabled_by: this._disabledBy,
|
||||||
new_entity_id: this._entityId.trim(),
|
new_entity_id: this._entityId.trim(),
|
||||||
@ -178,11 +184,27 @@ class DialogEntityRegistryDetail extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _deleteEntry(): Promise<void> {
|
private async _deleteEntry(): Promise<void> {
|
||||||
this._submitting = true;
|
if (
|
||||||
try {
|
!confirm(
|
||||||
if (await this._params!.removeEntry()) {
|
`${this.hass.localize(
|
||||||
this._params = undefined;
|
"ui.panel.config.entity_registry.editor.confirm_delete"
|
||||||
|
)}
|
||||||
|
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.entity_registry.editor.confirm_delete2",
|
||||||
|
"platform",
|
||||||
|
this._platform
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._submitting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await removeEntityRegistryEntry(this.hass!, this._entityId);
|
||||||
|
this._params = undefined;
|
||||||
} finally {
|
} finally {
|
||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,6 @@ import { HomeAssistant } from "../../../types";
|
|||||||
import {
|
import {
|
||||||
EntityRegistryEntry,
|
EntityRegistryEntry,
|
||||||
computeEntityRegistryName,
|
computeEntityRegistryName,
|
||||||
updateEntityRegistryEntry,
|
|
||||||
removeEntityRegistryEntry,
|
|
||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
@ -143,35 +141,6 @@ class HaConfigEntityRegistry extends LitElement {
|
|||||||
const entry = (ev.currentTarget! as any).entry;
|
const entry = (ev.currentTarget! as any).entry;
|
||||||
showEntityRegistryDetailDialog(this, {
|
showEntityRegistryDetailDialog(this, {
|
||||||
entry,
|
entry,
|
||||||
updateEntry: async (updates) => {
|
|
||||||
const updated = await updateEntityRegistryEntry(
|
|
||||||
this.hass!,
|
|
||||||
entry.entity_id,
|
|
||||||
updates
|
|
||||||
);
|
|
||||||
this._entities = this._entities!.map((ent) =>
|
|
||||||
ent === entry ? updated : ent
|
|
||||||
);
|
|
||||||
},
|
|
||||||
removeEntry: async () => {
|
|
||||||
if (
|
|
||||||
!confirm(`Are you sure you want to delete this entry?
|
|
||||||
|
|
||||||
Deleting an entry will not remove the entity from Home Assistant. To do this, you will need to remove the integration "${
|
|
||||||
entry.platform
|
|
||||||
}" from Home Assistant.`)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await removeEntityRegistryEntry(this.hass!, entry.entity_id);
|
|
||||||
this._entities = this._entities!.filter((ent) => ent !== entry);
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import {
|
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||||
EntityRegistryEntry,
|
|
||||||
EntityRegistryEntryUpdateParams,
|
|
||||||
} from "../../../data/entity_registry";
|
|
||||||
|
|
||||||
export interface EntityRegistryDetailDialogParams {
|
export interface EntityRegistryDetailDialogParams {
|
||||||
entry: EntityRegistryEntry;
|
entry: EntityRegistryEntry;
|
||||||
updateEntry: (
|
|
||||||
updates: Partial<EntityRegistryEntryUpdateParams>
|
|
||||||
) => Promise<unknown>;
|
|
||||||
removeEntry: () => Promise<boolean>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadEntityRegistryDetailDialog = () =>
|
export const loadEntityRegistryDetailDialog = () =>
|
||||||
|
@ -930,6 +930,8 @@
|
|||||||
"enabled_cause": "Disabled by {cause}.",
|
"enabled_cause": "Disabled by {cause}.",
|
||||||
"enabled_description": "Disabled entities will not be added to Home Assistant.",
|
"enabled_description": "Disabled entities will not be added to Home Assistant.",
|
||||||
"delete": "DELETE",
|
"delete": "DELETE",
|
||||||
|
"confirm_delete": "Are you sure you want to delete this entry?",
|
||||||
|
"confirm_delete2": "Deleting an entry will not remove the entity from Home Assistant. To do this, you will need to remove the integration '{platform}' from Home Assistant.",
|
||||||
"update": "UPDATE"
|
"update": "UPDATE"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user