Pass device ID to logbook if available (#12728)

This commit is contained in:
Paulus Schoutsen 2022-05-19 21:09:33 -07:00 committed by GitHub
parent 1f105b6c15
commit bfeb90780f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 291 additions and 225 deletions

View File

@ -56,17 +56,28 @@ export const getLogbookData = async (
hass: HomeAssistant,
startDate: string,
endDate: string,
entityId?: string
entityIds?: string[],
deviceIds?: string[]
): Promise<LogbookEntry[]> => {
const localize = await hass.loadBackendTranslation("device_class");
return addLogbookMessage(
hass,
localize,
await getLogbookDataCache(hass, startDate, endDate, entityId)
// bypass cache if we have a device ID
deviceIds?.length
? await getLogbookDataFromServer(
hass,
startDate,
endDate,
entityIds,
undefined,
deviceIds
)
: await getLogbookDataCache(hass, startDate, endDate, entityIds)
);
};
export const addLogbookMessage = (
const addLogbookMessage = (
hass: HomeAssistant,
localize: LocalizeFunc,
logbookData: LogbookEntry[]
@ -86,60 +97,73 @@ export const addLogbookMessage = (
return logbookData;
};
export const getLogbookDataCache = async (
const getLogbookDataCache = async (
hass: HomeAssistant,
startDate: string,
endDate: string,
entityId?: string
entityId?: string[]
) => {
const ALL_ENTITIES = "*";
if (!entityId) {
entityId = ALL_ENTITIES;
}
const entityIdKey = entityId ? entityId.toString() : ALL_ENTITIES;
const cacheKey = `${startDate}${endDate}`;
if (!DATA_CACHE[cacheKey]) {
DATA_CACHE[cacheKey] = {};
}
if (entityId in DATA_CACHE[cacheKey]) {
return DATA_CACHE[cacheKey][entityId];
if (entityIdKey in DATA_CACHE[cacheKey]) {
return DATA_CACHE[cacheKey][entityIdKey];
}
if (entityId !== ALL_ENTITIES && DATA_CACHE[cacheKey][ALL_ENTITIES]) {
if (entityId && DATA_CACHE[cacheKey][ALL_ENTITIES]) {
const entities = await DATA_CACHE[cacheKey][ALL_ENTITIES];
return entities.filter((entity) => entity.entity_id === entityId);
return entities.filter(
(entity) => entity.entity_id && entityId.includes(entity.entity_id)
);
}
DATA_CACHE[cacheKey][entityId] = getLogbookDataFromServer(
DATA_CACHE[cacheKey][entityIdKey] = getLogbookDataFromServer(
hass,
startDate,
endDate,
entityId !== ALL_ENTITIES ? entityId : undefined
).then((entries) => entries.reverse());
return DATA_CACHE[cacheKey][entityId];
entityId
);
return DATA_CACHE[cacheKey][entityIdKey];
};
export const getLogbookDataFromServer = (
const getLogbookDataFromServer = (
hass: HomeAssistant,
startDate: string,
endDate?: string,
entityId?: string,
contextId?: string
) => {
let params: any = {
entityIds?: string[],
contextId?: string,
deviceIds?: string[]
): Promise<LogbookEntry[]> => {
// If all specified filters are empty lists, we can return an empty list.
if (
(entityIds || deviceIds) &&
(!entityIds || entityIds.length === 0) &&
(!deviceIds || deviceIds.length === 0)
) {
return Promise.resolve([]);
}
const params: any = {
type: "logbook/get_events",
start_time: startDate,
};
if (endDate) {
params = { ...params, end_time: endDate };
params.end_time = endDate;
}
if (entityId) {
params = { ...params, entity_ids: entityId.split(",") };
} else if (contextId) {
params = { ...params, context_id: contextId };
if (entityIds?.length) {
params.entity_ids = entityIds;
}
if (deviceIds?.length) {
params.device_ids = deviceIds;
}
if (contextId) {
params.context_id = contextId;
}
return hass.callWS<LogbookEntry[]>(params);
};
@ -148,7 +172,7 @@ export const clearLogbookCache = (startDate: string, endDate: string) => {
DATA_CACHE[`${startDate}${endDate}`] = {};
};
export const getLogbookMessage = (
const getLogbookMessage = (
hass: HomeAssistant,
localize: LocalizeFunc,
state: string,

View File

@ -1,6 +1,7 @@
import { startOfYesterday } from "date-fns/esm";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { fireEvent } from "../../common/dom/fire_event";
import "../../panels/logbook/ha-logbook";
@ -16,6 +17,8 @@ export class MoreInfoLogbook extends LitElement {
private _time = { recent: 86400 };
private _entityIdAsList = memoizeOne((entityId: string) => [entityId]);
protected render(): TemplateResult {
if (!isComponentLoaded(this.hass, "logbook") || !this.entityId) {
return html``;
@ -38,7 +41,7 @@ export class MoreInfoLogbook extends LitElement {
<ha-logbook
.hass=${this.hass}
.time=${this._time}
.entityId=${this.entityId}
.entityIds=${this._entityIdAsList(this.entityId)}
narrow
no-icon
no-name

View File

@ -2,7 +2,10 @@ import "@material/mwc-button";
import { mdiImagePlus, mdiPencil } from "@mdi/js";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import { HassEntity } from "home-assistant-js-websocket/dist/types";
import {
HassEntity,
UnsubscribeFunc,
} from "home-assistant-js-websocket/dist/types";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
@ -20,6 +23,7 @@ import "../../logbook/ha-logbook";
import {
AreaRegistryEntry,
deleteAreaRegistryEntry,
subscribeAreaRegistry,
updateAreaRegistryEntry,
} from "../../../data/area_registry";
import { AutomationEntity } from "../../../data/automation";
@ -27,11 +31,13 @@ import {
computeDeviceName,
DeviceRegistryEntry,
sortDeviceRegistryByName,
subscribeDeviceRegistry,
} from "../../../data/device_registry";
import {
computeEntityRegistryName,
EntityRegistryEntry,
sortEntityRegistryByName,
subscribeEntityRegistry,
} from "../../../data/entity_registry";
import { SceneEntity } from "../../../data/scene";
import { ScriptEntity } from "../../../data/script";
@ -45,6 +51,7 @@ import {
loadAreaRegistryDetailDialog,
showAreaRegistryDetailDialog,
} from "./show-dialog-area-registry-detail";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
declare type NameAndEntity<EntityType extends HassEntity> = {
name: string;
@ -52,17 +59,11 @@ declare type NameAndEntity<EntityType extends HassEntity> = {
};
@customElement("ha-config-area-page")
class HaConfigAreaPage extends LitElement {
class HaConfigAreaPage extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public areaId!: string;
@property() public areas!: AreaRegistryEntry[];
@property() public devices!: DeviceRegistryEntry[];
@property() public entities!: EntityRegistryEntry[];
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
@property() public isWide!: boolean;
@ -71,6 +72,12 @@ class HaConfigAreaPage extends LitElement {
@property() public route!: Route;
@state() public _areas!: AreaRegistryEntry[];
@state() public _devices!: DeviceRegistryEntry[];
@state() public _entities!: EntityRegistryEntry[];
@state() private _related?: RelatedResult;
private _logbookTime = { recent: 86400 };
@ -89,7 +96,7 @@ class HaConfigAreaPage extends LitElement {
registryDevices: DeviceRegistryEntry[],
registryEntities: EntityRegistryEntry[]
) => {
const devices = new Map();
const devices = new Map<string, DeviceRegistryEntry>();
for (const device of registryDevices) {
if (device.area_id === areaId) {
@ -105,7 +112,7 @@ class HaConfigAreaPage extends LitElement {
if (entity.area_id === areaId) {
entities.push(entity);
}
} else if (devices.has(entity.device_id)) {
} else if (entity.device_id && devices.has(entity.device_id)) {
indirectEntities.push(entity);
}
}
@ -118,6 +125,10 @@ class HaConfigAreaPage extends LitElement {
}
);
private _allDeviceIds = memoizeOne((devices: DeviceRegistryEntry[]) =>
devices.map((device) => device.id)
);
private _allEntities = memoizeOne(
(memberships: {
entities: EntityRegistryEntry[];
@ -140,8 +151,26 @@ class HaConfigAreaPage extends LitElement {
}
}
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
subscribeAreaRegistry(this.hass.connection, (areas) => {
this._areas = areas;
}),
subscribeDeviceRegistry(this.hass.connection, (entries) => {
this._devices = entries;
}),
subscribeEntityRegistry(this.hass.connection, (entries) => {
this._entities = entries;
}),
];
}
protected render(): TemplateResult {
const area = this._area(this.areaId, this.areas);
if (!this._areas || !this._devices || !this._entities) {
return html``;
}
const area = this._area(this.areaId, this._areas);
if (!area) {
return html`
@ -154,8 +183,8 @@ class HaConfigAreaPage extends LitElement {
const memberships = this._memberships(
this.areaId,
this.devices,
this.entities
this._devices,
this._entities
);
const { devices, entities } = memberships;
@ -465,6 +494,7 @@ class HaConfigAreaPage extends LitElement {
.hass=${this.hass}
.time=${this._logbookTime}
.entityIds=${this._allEntities(memberships)}
.deviceIds=${this._allDeviceIds(memberships.devices)}
virtualize
narrow
no-icon

View File

@ -1,6 +1,7 @@
import { mdiHelpCircle, mdiPlus } from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import "../../../components/ha-fab";
@ -9,12 +10,20 @@ import "../../../components/ha-svg-icon";
import {
AreaRegistryEntry,
createAreaRegistryEntry,
subscribeAreaRegistry,
} from "../../../data/area_registry";
import type { DeviceRegistryEntry } from "../../../data/device_registry";
import type { EntityRegistryEntry } from "../../../data/entity_registry";
import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../../../data/device_registry";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../../data/entity_registry";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-loading-screen";
import "../../../layouts/hass-tabs-subpage";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../../types";
import "../ha-config-section";
import { configSections } from "../ha-panel-config";
@ -24,7 +33,7 @@ import {
} from "./show-dialog-area-registry-detail";
@customElement("ha-config-areas-dashboard")
export class HaConfigAreasDashboard extends LitElement {
export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public isWide?: boolean;
@ -33,13 +42,13 @@ export class HaConfigAreasDashboard extends LitElement {
@property() public route!: Route;
@property() public areas!: AreaRegistryEntry[];
@state() private _areas!: AreaRegistryEntry[];
@property() public devices!: DeviceRegistryEntry[];
@state() private _devices!: DeviceRegistryEntry[];
@property() public entities!: EntityRegistryEntry[];
@state() private _entities!: EntityRegistryEntry[];
private _areas = memoizeOne(
private _processAreas = memoizeOne(
(
areas: AreaRegistryEntry[],
devices: DeviceRegistryEntry[],
@ -75,6 +84,20 @@ export class HaConfigAreasDashboard extends LitElement {
})
);
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
return [
subscribeAreaRegistry(this.hass.connection, (areas) => {
this._areas = areas;
}),
subscribeDeviceRegistry(this.hass.connection, (entries) => {
this._devices = entries;
}),
subscribeEntityRegistry(this.hass.connection, (entries) => {
this._entities = entries;
}),
];
}
protected render(): TemplateResult {
return html`
<hass-tabs-subpage
@ -92,56 +115,62 @@ export class HaConfigAreasDashboard extends LitElement {
@click=${this._showHelp}
></ha-icon-button>
<div class="container">
${this._areas(this.areas, this.devices, this.entities).map(
(area) =>
html`<a href=${`/config/areas/area/${area.area_id}`}
><ha-card outlined>
<div
style=${styleMap({
backgroundImage: area.picture
? `url(${area.picture})`
: undefined,
})}
class="picture ${!area.picture ? "placeholder" : ""}"
></div>
<h1 class="card-header">${area.name}</h1>
<div class="card-content">
<div>
${area.devices
? html`
${this.hass.localize(
"ui.panel.config.integrations.config_entry.devices",
"count",
area.devices
)}${area.services ? "," : ""}
`
: ""}
${area.services
? html`
${this.hass.localize(
"ui.panel.config.integrations.config_entry.services",
"count",
area.services
)}
`
: ""}
${(area.devices || area.services) && area.entities
? this.hass.localize("ui.common.and")
: ""}
${area.entities
? html`
${this.hass.localize(
"ui.panel.config.integrations.config_entry.entities",
"count",
area.entities
)}
`
: ""}
</div>
</div>
</ha-card></a
>`
)}
${!this._areas || !this._devices || !this._entities
? ""
: this._processAreas(
this._areas,
this._devices,
this._entities
).map(
(area) =>
html`<a href=${`/config/areas/area/${area.area_id}`}
><ha-card outlined>
<div
style=${styleMap({
backgroundImage: area.picture
? `url(${area.picture})`
: undefined,
})}
class="picture ${!area.picture ? "placeholder" : ""}"
></div>
<h1 class="card-header">${area.name}</h1>
<div class="card-content">
<div>
${area.devices
? html`
${this.hass.localize(
"ui.panel.config.integrations.config_entry.devices",
"count",
area.devices
)}${area.services ? "," : ""}
`
: ""}
${area.services
? html`
${this.hass.localize(
"ui.panel.config.integrations.config_entry.services",
"count",
area.services
)}
`
: ""}
${(area.devices || area.services) && area.entities
? this.hass.localize("ui.common.and")
: ""}
${area.entities
? html`
${this.hass.localize(
"ui.panel.config.integrations.config_entry.entities",
"count",
area.entities
)}
`
: ""}
</div>
</div>
</ha-card></a
>`
)}
</div>
<ha-fab
slot="fab"

View File

@ -1,20 +1,4 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { stringCompare } from "../../../common/string/compare";
import {
AreaRegistryEntry,
subscribeAreaRegistry,
} from "../../../data/area_registry";
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../../../data/device_registry";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../../data/entity_registry";
import { customElement, property } from "lit/decorators";
import {
HassRouterPage,
RouterOptions,
@ -46,44 +30,6 @@ class HaConfigAreas extends HassRouterPage {
},
};
@state() private _configEntries: ConfigEntry[] = [];
@state()
private _deviceRegistryEntries: DeviceRegistryEntry[] = [];
@state()
private _entityRegistryEntries: EntityRegistryEntry[] = [];
@state() private _areas: AreaRegistryEntry[] = [];
private _unsubs?: UnsubscribeFunc[];
public connectedCallback() {
super.connectedCallback();
if (!this.hass) {
return;
}
this._loadData();
}
public disconnectedCallback() {
super.disconnectedCallback();
if (this._unsubs) {
while (this._unsubs.length) {
this._unsubs.pop()!();
}
this._unsubs = undefined;
}
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (!this._unsubs && changedProps.has("hass")) {
this._loadData();
}
}
protected updatePageEl(pageEl) {
pageEl.hass = this.hass;
@ -91,37 +37,11 @@ class HaConfigAreas extends HassRouterPage {
pageEl.areaId = this.routeTail.path.substr(1);
}
pageEl.entries = this._configEntries;
pageEl.devices = this._deviceRegistryEntries;
pageEl.entities = this._entityRegistryEntries;
pageEl.areas = this._areas;
pageEl.narrow = this.narrow;
pageEl.isWide = this.isWide;
pageEl.showAdvanced = this.showAdvanced;
pageEl.route = this.routeTail;
}
private _loadData() {
getConfigEntries(this.hass).then((configEntries) => {
this._configEntries = configEntries.sort((conf1, conf2) =>
stringCompare(conf1.title, conf2.title)
);
});
if (this._unsubs) {
return;
}
this._unsubs = [
subscribeAreaRegistry(this.hass.connection, (areas) => {
this._areas = areas;
}),
subscribeDeviceRegistry(this.hass.connection, (entries) => {
this._deviceRegistryEntries = entries;
}),
subscribeEntityRegistry(this.hass.connection, (entries) => {
this._entityRegistryEntries = entries;
}),
];
}
}
declare global {

View File

@ -134,6 +134,8 @@ export class HaConfigDevicePage extends LitElement {
)
);
private _deviceIdInList = memoizeOne((deviceId: string) => [deviceId]);
private _entityIds = memoizeOne(
(entries: EntityRegistryStateEntry[]): string[] =>
entries.map((entry) => entry.entity_id)
@ -592,7 +594,8 @@ export class HaConfigDevicePage extends LitElement {
<ha-logbook
.hass=${this.hass}
.time=${this._logbookTime}
.entityId=${this._entityIds(entities)}
.entityIds=${this._entityIds(entities)}
.deviceIds=${this._deviceIdInList(this.deviceId)}
virtualize
narrow
no-icon

View File

@ -21,7 +21,9 @@ export class HaLogbook extends LitElement {
@property() public time!: { range: [Date, Date] } | { recent: number };
@property() public entityId?: string | string[];
@property() public entityIds?: string[];
@property() public deviceIds?: string[];
@property({ type: Boolean, attribute: "narrow" })
public narrow = false;
@ -120,23 +122,47 @@ export class HaLogbook extends LitElement {
this._lastLogbookDate = undefined;
this._logbookEntries = undefined;
this._error = undefined;
this._throttleGetLogbookEntries();
}
protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (changedProps.has("time") || changedProps.has("entityId")) {
let changed = changedProps.has("time");
for (const key of ["entityIds", "deviceIds"]) {
if (!changedProps.has(key)) {
continue;
}
const oldValue = changedProps.get(key) as string[] | undefined;
const curValue = this[key] as string[] | undefined;
if (
!oldValue ||
!curValue ||
oldValue.length !== curValue.length ||
!oldValue.every((val) => curValue.includes(val))
) {
changed = true;
break;
}
}
if (changed) {
this.refresh(true);
return;
}
if (this._filterAlwaysEmptyResults) {
return;
}
// We only need to fetch again if we track recent entries for an entity
if (
!("recent" in this.time) ||
!changedProps.has("hass") ||
!this.entityId
!this.entityIds
) {
return;
}
@ -146,7 +172,7 @@ export class HaLogbook extends LitElement {
// Refresh data if we know the entity has changed.
if (
!oldHass ||
ensureArray(this.entityId).some(
ensureArray(this.entityIds).some(
(entityId) => this.hass.states[entityId] !== oldHass?.states[entityId]
)
) {
@ -155,9 +181,34 @@ export class HaLogbook extends LitElement {
}
}
private get _filterAlwaysEmptyResults(): boolean {
const entityIds = ensureArray(this.entityIds);
const deviceIds = ensureArray(this.deviceIds);
// If all specified filters are empty lists, we can return an empty list.
return (
(entityIds || deviceIds) &&
(!entityIds || entityIds.length === 0) &&
(!deviceIds || deviceIds.length === 0)
);
}
private async _getLogBookData() {
this._renderId += 1;
const renderId = this._renderId;
this._error = undefined;
if (this._filterAlwaysEmptyResults) {
this._logbookEntries = [];
this._lastLogbookDate = undefined;
return;
}
this._updateUsers();
if (this.hass.user?.is_admin) {
this._updateTraceContexts();
}
let startTime: Date;
let endTime: Date;
let appendData = false;
@ -173,34 +224,21 @@ export class HaLogbook extends LitElement {
endTime = new Date();
}
const entityIdFilter = this.entityId
? ensureArray(this.entityId)
: undefined;
let newEntries: LogbookEntry[];
if (entityIdFilter?.length === 0) {
// filtering by 0 entities, means we never can have any results
newEntries = [];
} else {
this._updateUsers();
if (this.hass.user?.is_admin) {
this._updateTraceContexts();
}
try {
newEntries = await getLogbookData(
this.hass,
startTime.toISOString(),
endTime.toISOString(),
entityIdFilter ? entityIdFilter.toString() : undefined
);
} catch (err: any) {
if (renderId === this._renderId) {
this._error = err.message;
}
return;
try {
newEntries = await getLogbookData(
this.hass,
startTime.toISOString(),
endTime.toISOString(),
ensureArray(this.entityIds),
ensureArray(this.deviceIds)
);
} catch (err: any) {
if (renderId === this._renderId) {
this._error = err.message;
}
return;
}
// New render happening.
@ -208,6 +246,10 @@ export class HaLogbook extends LitElement {
return;
}
// Put newest ones on top. Reverse works in-place so
// make a copy first.
newEntries = [...newEntries].reverse();
this._logbookEntries =
appendData && this._logbookEntries
? newEntries.concat(...this._logbookEntries)

View File

@ -38,7 +38,7 @@ export class HaPanelLogbook extends LitElement {
@state() _time: { range: [Date, Date] };
@state() _entityId = "";
@state() _entityIds?: string[];
@property({ reflect: true, type: Boolean }) rtl = false;
@ -85,7 +85,7 @@ export class HaPanelLogbook extends LitElement {
<ha-entity-picker
.hass=${this.hass}
.value=${this._entityId}
.value=${this._entityIds ? this._entityIds[0] : undefined}
.label=${this.hass.localize(
"ui.components.entity.entity-picker.entity"
)}
@ -97,7 +97,7 @@ export class HaPanelLogbook extends LitElement {
<ha-logbook
.hass=${this.hass}
.time=${this._time}
.entityId=${this._entityId}
.entityIds=${this._entityIds}
virtualize
></ha-logbook>
</ha-app-layout>
@ -157,15 +157,30 @@ export class HaPanelLogbook extends LitElement {
this.rtl = computeRTL(this.hass);
}
}
this._applyURLParams();
}
private _applyURLParams() {
const searchParams = new URLSearchParams(location.search);
if (searchParams.has("entity_id")) {
this._entityId = searchParams.get("entity_id") ?? "";
const entityIdsRaw = searchParams.get("entity_id");
if (!entityIdsRaw) {
this._entityIds = undefined;
} else {
const entityIds = entityIdsRaw.split(",").sort();
// Check if different
if (
!this._entityIds ||
entityIds.length !== this._entityIds.length ||
this._entityIds.every((val, idx) => val === entityIds[idx])
) {
this._entityIds = entityIds;
}
}
} else {
this._entityIds = undefined;
}
const startDateStr = searchParams.get("start_date");
@ -199,19 +214,19 @@ export class HaPanelLogbook extends LitElement {
endDate.setDate(endDate.getDate() + 1);
endDate.setMilliseconds(endDate.getMilliseconds() - 1);
}
this._time = { range: [startDate, endDate] };
this._updatePath({
start_date: this._time.range[0].toISOString(),
end_date: this._time.range[1].toISOString(),
start_date: startDate.toISOString(),
end_date: endDate.toISOString(),
});
}
private _entityPicked(ev) {
this._entityId = ev.target.value;
this._updatePath({ entity_id: this._entityId });
this._updatePath({
entity_id: ev.target.value || undefined,
});
}
private _updatePath(update: Record<string, string>) {
private _updatePath(update: Record<string, string | undefined>) {
const params = extractSearchParamsObject();
for (const [key, value] of Object.entries(update)) {
if (value === undefined) {

View File

@ -120,7 +120,7 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
<ha-logbook
.hass=${this.hass}
.time=${this._time}
.entityId=${this._entityId}
.entityIds=${this._entityId}
narrow
relative-time
virtualize