Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions src/panels/config/devices/device-detail/ha-device-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,24 +123,26 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
</template>
</div>

<div class="device-entities">
<template
is="dom-repeat"
items="[[_computeDeviceEntities(hass, device, entities)]]"
as="entity"
>
<paper-icon-item on-click="_openMoreInfo">
<state-badge
state-obj="[[_computeStateObj(entity, hass)]]"
slot="item-icon"
></state-badge>
<paper-item-body>
<div class="name">[[_computeEntityName(entity, hass)]]</div>
<div class="secondary entity-id">[[entity.entity_id]]</div>
</paper-item-body>
</paper-icon-item>
</template>
</div>
<template is="dom-if" if="[[!hideEntities]]">
<div class="device-entities">
<template
is="dom-repeat"
items="[[_computeDeviceEntities(hass, device, entities)]]"
as="entity"
>
<paper-icon-item on-click="_openMoreInfo">
<state-badge
state-obj="[[_computeStateObj(entity, hass)]]"
slot="item-icon"
></state-badge>
<paper-item-body>
<div class="name">[[_computeEntityName(entity, hass)]]</div>
<div class="secondary entity-id">[[entity.entity_id]]</div>
</paper-item-body>
</paper-icon-item>
</template>
</div>
</template>
</ha-card>
`;
}
Expand All @@ -157,6 +159,7 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
reflectToAttribute: true,
},
hideSettings: { type: Boolean, value: false },
hideEntities: { type: Boolean, value: false },
_childDevices: {
type: Array,
computed: "_computeChildDevices(device, devices)",
Expand Down
175 changes: 175 additions & 0 deletions src/panels/config/devices/device-detail/ha-device-entities-card.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
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-icon-item";
import "@polymer/paper-item/paper-item-body";

import "../../../../components/ha-card";
import "../../../../components/ha-icon";
import { computeStateName } from "../../../../common/entity/compute_state_name";
import {
EntityRegistryEntry,
updateEntityRegistryEntry,
removeEntityRegistryEntry,
} 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";

@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;

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>
${entities.length
? entities.map((entry: EntityRegistryEntry) => {
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 _openEditEntry(ev: MouseEvent): void {
const entry = (ev.currentTarget! as any).closest("paper-icon-item").entry;
showEntityRegistryDetailDialog(this, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird that this is necessary. The update/confirm logic should happen in the dialog and we just should receive callbacks.

entry,
updateEntry: async (updates) => {
await updateEntityRegistryEntry(this.hass!, entry.entity_id, updates);
},
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);
return true;
} catch (err) {
return false;
}
},
});
}

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);
}
`;
}
}
41 changes: 33 additions & 8 deletions src/panels/config/devices/ha-config-device-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import memoizeOne from "memoize-one";

import "../../../layouts/hass-subpage";
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-conditions-card";
import "./device-detail/ha-device-actions-card";
import "./device-detail/ha-device-entities-card";
import { HomeAssistant } from "../../../types";
import { ConfigEntry } from "../../../data/config_entries";
import { EntityRegistryEntry } from "../../../data/entity_registry";
Expand All @@ -37,6 +38,7 @@ export class HaConfigDevicePage extends LitElement {
@property() public entities!: EntityRegistryEntry[];
@property() public areas!: AreaRegistryEntry[];
@property() public deviceId!: string;
@property() public narrow!: boolean;

private _device = memoizeOne(
(
Expand Down Expand Up @@ -67,15 +69,22 @@ export class HaConfigDevicePage extends LitElement {
icon="hass:settings"
@click=${this._showSettings}
></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
.hass=${this.hass}
.areas=${this.areas}
.devices=${this.devices}
.device=${device}
.entities=${this.entities}
hide-settings
hide-entities
></ha-device-card>

<div class="header">Automations</div>
<ha-device-triggers-card
.hass=${this.hass}
.deviceId=${this.deviceId}
Expand All @@ -88,7 +97,15 @@ export class HaConfigDevicePage extends LitElement {
.hass=${this.hass}
.deviceId=${this.deviceId}
></ha-device-actions-card>
</div>

<div class="header">Entities</div>
<ha-device-entities-card
.hass=${this.hass}
.deviceId=${this.deviceId}
.entities=${this.entities}
>
</ha-device-entities-card>
</ha-config-section>
</hass-subpage>
`;
}
Expand All @@ -104,12 +121,20 @@ export class HaConfigDevicePage extends LitElement {

static get styles(): CSSResult {
return css`
.content {
padding: 16px;
.header {
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;
margin-bottom: 16px;

ha-config-section *:last-child {
padding-bottom: 24px;
}
`;
}
Expand Down