mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-22 00:36:34 +00:00
Show entities on area page (#8980)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
This commit is contained in:
parent
9690434cac
commit
7962130a0c
@ -22,12 +22,16 @@ import {
|
|||||||
import {
|
import {
|
||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
devicesInArea,
|
|
||||||
} from "../../../data/device_registry";
|
} from "../../../data/device_registry";
|
||||||
|
import {
|
||||||
|
computeEntityRegistryName,
|
||||||
|
EntityRegistryEntry,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
import { findRelated, RelatedResult } from "../../../data/search";
|
import { findRelated, RelatedResult } from "../../../data/search";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
|
import { showEntityEditorDialog } from "../entities/show-dialog-entity-editor";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import {
|
import {
|
||||||
loadAreaRegistryDetailDialog,
|
loadAreaRegistryDetailDialog,
|
||||||
@ -44,6 +48,8 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
|
|
||||||
@property() public devices!: DeviceRegistryEntry[];
|
@property() public devices!: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
@property() public entities!: EntityRegistryEntry[];
|
||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
|
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
|
||||||
|
|
||||||
@property() public isWide!: boolean;
|
@property() public isWide!: boolean;
|
||||||
@ -58,9 +64,39 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
| AreaRegistryEntry
|
| AreaRegistryEntry
|
||||||
| undefined => areas.find((area) => area.area_id === areaId));
|
| undefined => areas.find((area) => area.area_id === areaId));
|
||||||
|
|
||||||
private _devices = memoizeOne(
|
private _memberships = memoizeOne(
|
||||||
(areaId: string, devices: DeviceRegistryEntry[]): DeviceRegistryEntry[] =>
|
(
|
||||||
devicesInArea(devices, areaId)
|
areaId: string,
|
||||||
|
registryDevices: DeviceRegistryEntry[],
|
||||||
|
registryEntities: EntityRegistryEntry[]
|
||||||
|
) => {
|
||||||
|
const devices = new Map();
|
||||||
|
|
||||||
|
for (const device of registryDevices) {
|
||||||
|
if (device.area_id === areaId) {
|
||||||
|
devices.set(device.id, device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const entities: EntityRegistryEntry[] = [];
|
||||||
|
const indirectEntities: EntityRegistryEntry[] = [];
|
||||||
|
|
||||||
|
for (const entity of registryEntities) {
|
||||||
|
if (entity.area_id) {
|
||||||
|
if (entity.area_id === areaId) {
|
||||||
|
entities.push(entity);
|
||||||
|
}
|
||||||
|
} else if (devices.has(entity.device_id)) {
|
||||||
|
indirectEntities.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
devices: Array.from(devices.values()),
|
||||||
|
entities,
|
||||||
|
indirectEntities,
|
||||||
|
};
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
@ -87,7 +123,11 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const devices = this._devices(this.areaId, this.devices);
|
const { devices, entities } = this._memberships(
|
||||||
|
this.areaId,
|
||||||
|
this.devices,
|
||||||
|
this.entities
|
||||||
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
@ -144,6 +184,33 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
>
|
>
|
||||||
`}
|
`}
|
||||||
</ha-card>
|
</ha-card>
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.areas.editor.linked_entities_caption"
|
||||||
|
)}
|
||||||
|
>${entities.length
|
||||||
|
? entities.map(
|
||||||
|
(entity) =>
|
||||||
|
html`
|
||||||
|
<paper-item
|
||||||
|
@click=${this._openEntity}
|
||||||
|
.entity=${entity}
|
||||||
|
>
|
||||||
|
<paper-item-body>
|
||||||
|
${computeEntityRegistryName(this.hass, entity)}
|
||||||
|
</paper-item-body>
|
||||||
|
<ha-icon-next></ha-icon-next>
|
||||||
|
</paper-item>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
: html`
|
||||||
|
<paper-item class="no-link"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.areas.editor.no_linked_entities"
|
||||||
|
)}</paper-item
|
||||||
|
>
|
||||||
|
`}
|
||||||
|
</ha-card>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
${isComponentLoaded(this.hass, "automation")
|
${isComponentLoaded(this.hass, "automation")
|
||||||
@ -299,6 +366,14 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
this._openDialog(entry);
|
this._openDialog(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _openEntity(ev) {
|
||||||
|
const entry: EntityRegistryEntry = (ev.currentTarget as any).entity;
|
||||||
|
showEntityEditorDialog(this, {
|
||||||
|
entity_id: entry.entity_id,
|
||||||
|
entry,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _openDialog(entry?: AreaRegistryEntry) {
|
private _openDialog(entry?: AreaRegistryEntry) {
|
||||||
showAreaRegistryDetailDialog(this, {
|
showAreaRegistryDetailDialog(this, {
|
||||||
entry,
|
entry,
|
||||||
|
@ -24,10 +24,8 @@ import {
|
|||||||
AreaRegistryEntry,
|
AreaRegistryEntry,
|
||||||
createAreaRegistryEntry,
|
createAreaRegistryEntry,
|
||||||
} from "../../../data/area_registry";
|
} from "../../../data/area_registry";
|
||||||
import {
|
import type { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||||
DeviceRegistryEntry,
|
import type { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||||
devicesInArea,
|
|
||||||
} from "../../../data/device_registry";
|
|
||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-loading-screen";
|
import "../../../layouts/hass-loading-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage-data-table";
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
@ -53,12 +51,39 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
|
|
||||||
@property() public devices!: DeviceRegistryEntry[];
|
@property() public devices!: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
@property() public entities!: EntityRegistryEntry[];
|
||||||
|
|
||||||
private _areas = memoizeOne(
|
private _areas = memoizeOne(
|
||||||
(areas: AreaRegistryEntry[], devices: DeviceRegistryEntry[]) => {
|
(
|
||||||
|
areas: AreaRegistryEntry[],
|
||||||
|
devices: DeviceRegistryEntry[],
|
||||||
|
entities: EntityRegistryEntry[]
|
||||||
|
) => {
|
||||||
return areas.map((area) => {
|
return areas.map((area) => {
|
||||||
|
const devicesInArea = new Set();
|
||||||
|
|
||||||
|
for (const device of devices) {
|
||||||
|
if (device.area_id === area.area_id) {
|
||||||
|
devicesInArea.add(device.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let entitiesInArea = 0;
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
if (
|
||||||
|
entity.area_id
|
||||||
|
? entity.area_id === area.area_id
|
||||||
|
: devicesInArea.has(entity.device_id)
|
||||||
|
) {
|
||||||
|
entitiesInArea++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...area,
|
...area,
|
||||||
devices: devicesInArea(devices, area.area_id).length,
|
devices: devicesInArea.size,
|
||||||
|
entities: entitiesInArea,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -97,6 +122,15 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
width: "20%",
|
width: "20%",
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
},
|
},
|
||||||
|
entities: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.areas.data_table.entities"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
type: "numeric",
|
||||||
|
width: "20%",
|
||||||
|
direction: "asc",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -110,7 +144,7 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
.tabs=${configSections.integrations}
|
.tabs=${configSections.integrations}
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.columns=${this._columns(this.narrow)}
|
.columns=${this._columns(this.narrow)}
|
||||||
.data=${this._areas(this.areas, this.devices)}
|
.data=${this._areas(this.areas, this.devices, this.entities)}
|
||||||
@row-click=${this._handleRowClicked}
|
@row-click=${this._handleRowClicked}
|
||||||
.noDataText=${this.hass.localize(
|
.noDataText=${this.hass.localize(
|
||||||
"ui.panel.config.areas.picker.no_areas"
|
"ui.panel.config.areas.picker.no_areas"
|
||||||
|
@ -15,6 +15,10 @@ import {
|
|||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
subscribeDeviceRegistry,
|
subscribeDeviceRegistry,
|
||||||
} from "../../../data/device_registry";
|
} from "../../../data/device_registry";
|
||||||
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
import {
|
import {
|
||||||
HassRouterPage,
|
HassRouterPage,
|
||||||
RouterOptions,
|
RouterOptions,
|
||||||
@ -51,6 +55,9 @@ class HaConfigAreas extends HassRouterPage {
|
|||||||
@internalProperty()
|
@internalProperty()
|
||||||
private _deviceRegistryEntries: DeviceRegistryEntry[] = [];
|
private _deviceRegistryEntries: DeviceRegistryEntry[] = [];
|
||||||
|
|
||||||
|
@internalProperty()
|
||||||
|
private _entityRegistryEntries: EntityRegistryEntry[] = [];
|
||||||
|
|
||||||
@internalProperty() private _areas: AreaRegistryEntry[] = [];
|
@internalProperty() private _areas: AreaRegistryEntry[] = [];
|
||||||
|
|
||||||
private _unsubs?: UnsubscribeFunc[];
|
private _unsubs?: UnsubscribeFunc[];
|
||||||
@ -90,6 +97,7 @@ class HaConfigAreas extends HassRouterPage {
|
|||||||
|
|
||||||
pageEl.entries = this._configEntries;
|
pageEl.entries = this._configEntries;
|
||||||
pageEl.devices = this._deviceRegistryEntries;
|
pageEl.devices = this._deviceRegistryEntries;
|
||||||
|
pageEl.entities = this._entityRegistryEntries;
|
||||||
pageEl.areas = this._areas;
|
pageEl.areas = this._areas;
|
||||||
pageEl.narrow = this.narrow;
|
pageEl.narrow = this.narrow;
|
||||||
pageEl.isWide = this.isWide;
|
pageEl.isWide = this.isWide;
|
||||||
@ -113,6 +121,9 @@ class HaConfigAreas extends HassRouterPage {
|
|||||||
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
||||||
this._deviceRegistryEntries = entries;
|
this._deviceRegistryEntries = entries;
|
||||||
}),
|
}),
|
||||||
|
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||||
|
this._entityRegistryEntries = entries;
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -905,7 +905,8 @@
|
|||||||
"description": "Group devices and entities into areas",
|
"description": "Group devices and entities into areas",
|
||||||
"data_table": {
|
"data_table": {
|
||||||
"area": "Area",
|
"area": "Area",
|
||||||
"devices": "Devices"
|
"devices": "Devices",
|
||||||
|
"entities": "Entities"
|
||||||
},
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Areas",
|
"header": "Areas",
|
||||||
@ -923,7 +924,9 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"name_required": "Name is required",
|
"name_required": "Name is required",
|
||||||
"area_id": "Area ID",
|
"area_id": "Area ID",
|
||||||
"unknown_error": "Unknown error"
|
"unknown_error": "Unknown error",
|
||||||
|
"linked_entities_caption": "Entities",
|
||||||
|
"no_linked_entities": "There are no entities linked to this area."
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"confirmation_title": "Are you sure you want to delete this area?",
|
"confirmation_title": "Are you sure you want to delete this area?",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user