mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-28 07:17:21 +00:00
Pass device ID to logbook if available (#12728)
This commit is contained in:
parent
1f105b6c15
commit
bfeb90780f
@ -56,17 +56,28 @@ export const getLogbookData = async (
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate: string,
|
endDate: string,
|
||||||
entityId?: string
|
entityIds?: string[],
|
||||||
|
deviceIds?: string[]
|
||||||
): Promise<LogbookEntry[]> => {
|
): Promise<LogbookEntry[]> => {
|
||||||
const localize = await hass.loadBackendTranslation("device_class");
|
const localize = await hass.loadBackendTranslation("device_class");
|
||||||
return addLogbookMessage(
|
return addLogbookMessage(
|
||||||
hass,
|
hass,
|
||||||
localize,
|
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,
|
hass: HomeAssistant,
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
logbookData: LogbookEntry[]
|
logbookData: LogbookEntry[]
|
||||||
@ -86,60 +97,73 @@ export const addLogbookMessage = (
|
|||||||
return logbookData;
|
return logbookData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLogbookDataCache = async (
|
const getLogbookDataCache = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate: string,
|
endDate: string,
|
||||||
entityId?: string
|
entityId?: string[]
|
||||||
) => {
|
) => {
|
||||||
const ALL_ENTITIES = "*";
|
const ALL_ENTITIES = "*";
|
||||||
|
|
||||||
if (!entityId) {
|
const entityIdKey = entityId ? entityId.toString() : ALL_ENTITIES;
|
||||||
entityId = ALL_ENTITIES;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cacheKey = `${startDate}${endDate}`;
|
const cacheKey = `${startDate}${endDate}`;
|
||||||
|
|
||||||
if (!DATA_CACHE[cacheKey]) {
|
if (!DATA_CACHE[cacheKey]) {
|
||||||
DATA_CACHE[cacheKey] = {};
|
DATA_CACHE[cacheKey] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entityId in DATA_CACHE[cacheKey]) {
|
if (entityIdKey in DATA_CACHE[cacheKey]) {
|
||||||
return DATA_CACHE[cacheKey][entityId];
|
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];
|
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,
|
hass,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
entityId !== ALL_ENTITIES ? entityId : undefined
|
entityId
|
||||||
).then((entries) => entries.reverse());
|
);
|
||||||
return DATA_CACHE[cacheKey][entityId];
|
return DATA_CACHE[cacheKey][entityIdKey];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLogbookDataFromServer = (
|
const getLogbookDataFromServer = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate?: string,
|
endDate?: string,
|
||||||
entityId?: string,
|
entityIds?: string[],
|
||||||
contextId?: string
|
contextId?: string,
|
||||||
) => {
|
deviceIds?: string[]
|
||||||
let params: any = {
|
): 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",
|
type: "logbook/get_events",
|
||||||
start_time: startDate,
|
start_time: startDate,
|
||||||
};
|
};
|
||||||
if (endDate) {
|
if (endDate) {
|
||||||
params = { ...params, end_time: endDate };
|
params.end_time = endDate;
|
||||||
}
|
}
|
||||||
if (entityId) {
|
if (entityIds?.length) {
|
||||||
params = { ...params, entity_ids: entityId.split(",") };
|
params.entity_ids = entityIds;
|
||||||
} else if (contextId) {
|
}
|
||||||
params = { ...params, context_id: contextId };
|
if (deviceIds?.length) {
|
||||||
|
params.device_ids = deviceIds;
|
||||||
|
}
|
||||||
|
if (contextId) {
|
||||||
|
params.context_id = contextId;
|
||||||
}
|
}
|
||||||
return hass.callWS<LogbookEntry[]>(params);
|
return hass.callWS<LogbookEntry[]>(params);
|
||||||
};
|
};
|
||||||
@ -148,7 +172,7 @@ export const clearLogbookCache = (startDate: string, endDate: string) => {
|
|||||||
DATA_CACHE[`${startDate}${endDate}`] = {};
|
DATA_CACHE[`${startDate}${endDate}`] = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLogbookMessage = (
|
const getLogbookMessage = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
state: string,
|
state: string,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { startOfYesterday } from "date-fns/esm";
|
import { startOfYesterday } from "date-fns/esm";
|
||||||
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../panels/logbook/ha-logbook";
|
import "../../panels/logbook/ha-logbook";
|
||||||
@ -16,6 +17,8 @@ export class MoreInfoLogbook extends LitElement {
|
|||||||
|
|
||||||
private _time = { recent: 86400 };
|
private _time = { recent: 86400 };
|
||||||
|
|
||||||
|
private _entityIdAsList = memoizeOne((entityId: string) => [entityId]);
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!isComponentLoaded(this.hass, "logbook") || !this.entityId) {
|
if (!isComponentLoaded(this.hass, "logbook") || !this.entityId) {
|
||||||
return html``;
|
return html``;
|
||||||
@ -38,7 +41,7 @@ export class MoreInfoLogbook extends LitElement {
|
|||||||
<ha-logbook
|
<ha-logbook
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.time=${this._time}
|
.time=${this._time}
|
||||||
.entityId=${this.entityId}
|
.entityIds=${this._entityIdAsList(this.entityId)}
|
||||||
narrow
|
narrow
|
||||||
no-icon
|
no-icon
|
||||||
no-name
|
no-name
|
||||||
|
@ -2,7 +2,10 @@ import "@material/mwc-button";
|
|||||||
import { mdiImagePlus, mdiPencil } from "@mdi/js";
|
import { mdiImagePlus, mdiPencil } from "@mdi/js";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
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 { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
@ -20,6 +23,7 @@ import "../../logbook/ha-logbook";
|
|||||||
import {
|
import {
|
||||||
AreaRegistryEntry,
|
AreaRegistryEntry,
|
||||||
deleteAreaRegistryEntry,
|
deleteAreaRegistryEntry,
|
||||||
|
subscribeAreaRegistry,
|
||||||
updateAreaRegistryEntry,
|
updateAreaRegistryEntry,
|
||||||
} from "../../../data/area_registry";
|
} from "../../../data/area_registry";
|
||||||
import { AutomationEntity } from "../../../data/automation";
|
import { AutomationEntity } from "../../../data/automation";
|
||||||
@ -27,11 +31,13 @@ import {
|
|||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
sortDeviceRegistryByName,
|
sortDeviceRegistryByName,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
} from "../../../data/device_registry";
|
} from "../../../data/device_registry";
|
||||||
import {
|
import {
|
||||||
computeEntityRegistryName,
|
computeEntityRegistryName,
|
||||||
EntityRegistryEntry,
|
EntityRegistryEntry,
|
||||||
sortEntityRegistryByName,
|
sortEntityRegistryByName,
|
||||||
|
subscribeEntityRegistry,
|
||||||
} from "../../../data/entity_registry";
|
} from "../../../data/entity_registry";
|
||||||
import { SceneEntity } from "../../../data/scene";
|
import { SceneEntity } from "../../../data/scene";
|
||||||
import { ScriptEntity } from "../../../data/script";
|
import { ScriptEntity } from "../../../data/script";
|
||||||
@ -45,6 +51,7 @@ import {
|
|||||||
loadAreaRegistryDetailDialog,
|
loadAreaRegistryDetailDialog,
|
||||||
showAreaRegistryDetailDialog,
|
showAreaRegistryDetailDialog,
|
||||||
} from "./show-dialog-area-registry-detail";
|
} from "./show-dialog-area-registry-detail";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
|
|
||||||
declare type NameAndEntity<EntityType extends HassEntity> = {
|
declare type NameAndEntity<EntityType extends HassEntity> = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -52,17 +59,11 @@ declare type NameAndEntity<EntityType extends HassEntity> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@customElement("ha-config-area-page")
|
@customElement("ha-config-area-page")
|
||||||
class HaConfigAreaPage extends LitElement {
|
class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public areaId!: string;
|
@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({ type: Boolean, reflect: true }) public narrow!: boolean;
|
||||||
|
|
||||||
@property() public isWide!: boolean;
|
@property() public isWide!: boolean;
|
||||||
@ -71,6 +72,12 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
|
|
||||||
@property() public route!: Route;
|
@property() public route!: Route;
|
||||||
|
|
||||||
|
@state() public _areas!: AreaRegistryEntry[];
|
||||||
|
|
||||||
|
@state() public _devices!: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
@state() public _entities!: EntityRegistryEntry[];
|
||||||
|
|
||||||
@state() private _related?: RelatedResult;
|
@state() private _related?: RelatedResult;
|
||||||
|
|
||||||
private _logbookTime = { recent: 86400 };
|
private _logbookTime = { recent: 86400 };
|
||||||
@ -89,7 +96,7 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
registryDevices: DeviceRegistryEntry[],
|
registryDevices: DeviceRegistryEntry[],
|
||||||
registryEntities: EntityRegistryEntry[]
|
registryEntities: EntityRegistryEntry[]
|
||||||
) => {
|
) => {
|
||||||
const devices = new Map();
|
const devices = new Map<string, DeviceRegistryEntry>();
|
||||||
|
|
||||||
for (const device of registryDevices) {
|
for (const device of registryDevices) {
|
||||||
if (device.area_id === areaId) {
|
if (device.area_id === areaId) {
|
||||||
@ -105,7 +112,7 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
if (entity.area_id === areaId) {
|
if (entity.area_id === areaId) {
|
||||||
entities.push(entity);
|
entities.push(entity);
|
||||||
}
|
}
|
||||||
} else if (devices.has(entity.device_id)) {
|
} else if (entity.device_id && devices.has(entity.device_id)) {
|
||||||
indirectEntities.push(entity);
|
indirectEntities.push(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +125,10 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private _allDeviceIds = memoizeOne((devices: DeviceRegistryEntry[]) =>
|
||||||
|
devices.map((device) => device.id)
|
||||||
|
);
|
||||||
|
|
||||||
private _allEntities = memoizeOne(
|
private _allEntities = memoizeOne(
|
||||||
(memberships: {
|
(memberships: {
|
||||||
entities: EntityRegistryEntry[];
|
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 {
|
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) {
|
if (!area) {
|
||||||
return html`
|
return html`
|
||||||
@ -154,8 +183,8 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
|
|
||||||
const memberships = this._memberships(
|
const memberships = this._memberships(
|
||||||
this.areaId,
|
this.areaId,
|
||||||
this.devices,
|
this._devices,
|
||||||
this.entities
|
this._entities
|
||||||
);
|
);
|
||||||
const { devices, entities } = memberships;
|
const { devices, entities } = memberships;
|
||||||
|
|
||||||
@ -465,6 +494,7 @@ class HaConfigAreaPage extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.time=${this._logbookTime}
|
.time=${this._logbookTime}
|
||||||
.entityIds=${this._allEntities(memberships)}
|
.entityIds=${this._allEntities(memberships)}
|
||||||
|
.deviceIds=${this._allDeviceIds(memberships.devices)}
|
||||||
virtualize
|
virtualize
|
||||||
narrow
|
narrow
|
||||||
no-icon
|
no-icon
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { mdiHelpCircle, mdiPlus } from "@mdi/js";
|
import { mdiHelpCircle, mdiPlus } from "@mdi/js";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
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 { styleMap } from "lit/directives/style-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
@ -9,12 +10,20 @@ import "../../../components/ha-svg-icon";
|
|||||||
import {
|
import {
|
||||||
AreaRegistryEntry,
|
AreaRegistryEntry,
|
||||||
createAreaRegistryEntry,
|
createAreaRegistryEntry,
|
||||||
|
subscribeAreaRegistry,
|
||||||
} from "../../../data/area_registry";
|
} from "../../../data/area_registry";
|
||||||
import type { DeviceRegistryEntry } from "../../../data/device_registry";
|
import {
|
||||||
import type { EntityRegistryEntry } from "../../../data/entity_registry";
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
} from "../../../data/entity_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";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
@ -24,7 +33,7 @@ import {
|
|||||||
} from "./show-dialog-area-registry-detail";
|
} from "./show-dialog-area-registry-detail";
|
||||||
|
|
||||||
@customElement("ha-config-areas-dashboard")
|
@customElement("ha-config-areas-dashboard")
|
||||||
export class HaConfigAreasDashboard extends LitElement {
|
export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public isWide?: boolean;
|
@property() public isWide?: boolean;
|
||||||
@ -33,13 +42,13 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
|
|
||||||
@property() public route!: Route;
|
@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[],
|
areas: AreaRegistryEntry[],
|
||||||
devices: DeviceRegistryEntry[],
|
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 {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
@ -92,7 +115,13 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
@click=${this._showHelp}
|
@click=${this._showHelp}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
${this._areas(this.areas, this.devices, this.entities).map(
|
${!this._areas || !this._devices || !this._entities
|
||||||
|
? ""
|
||||||
|
: this._processAreas(
|
||||||
|
this._areas,
|
||||||
|
this._devices,
|
||||||
|
this._entities
|
||||||
|
).map(
|
||||||
(area) =>
|
(area) =>
|
||||||
html`<a href=${`/config/areas/area/${area.area_id}`}
|
html`<a href=${`/config/areas/area/${area.area_id}`}
|
||||||
><ha-card outlined>
|
><ha-card outlined>
|
||||||
|
@ -1,20 +1,4 @@
|
|||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { customElement, property } from "lit/decorators";
|
||||||
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 {
|
import {
|
||||||
HassRouterPage,
|
HassRouterPage,
|
||||||
RouterOptions,
|
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) {
|
protected updatePageEl(pageEl) {
|
||||||
pageEl.hass = this.hass;
|
pageEl.hass = this.hass;
|
||||||
|
|
||||||
@ -91,37 +37,11 @@ class HaConfigAreas extends HassRouterPage {
|
|||||||
pageEl.areaId = this.routeTail.path.substr(1);
|
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.narrow = this.narrow;
|
||||||
pageEl.isWide = this.isWide;
|
pageEl.isWide = this.isWide;
|
||||||
pageEl.showAdvanced = this.showAdvanced;
|
pageEl.showAdvanced = this.showAdvanced;
|
||||||
pageEl.route = this.routeTail;
|
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 {
|
declare global {
|
||||||
|
@ -134,6 +134,8 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private _deviceIdInList = memoizeOne((deviceId: string) => [deviceId]);
|
||||||
|
|
||||||
private _entityIds = memoizeOne(
|
private _entityIds = memoizeOne(
|
||||||
(entries: EntityRegistryStateEntry[]): string[] =>
|
(entries: EntityRegistryStateEntry[]): string[] =>
|
||||||
entries.map((entry) => entry.entity_id)
|
entries.map((entry) => entry.entity_id)
|
||||||
@ -592,7 +594,8 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
<ha-logbook
|
<ha-logbook
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.time=${this._logbookTime}
|
.time=${this._logbookTime}
|
||||||
.entityId=${this._entityIds(entities)}
|
.entityIds=${this._entityIds(entities)}
|
||||||
|
.deviceIds=${this._deviceIdInList(this.deviceId)}
|
||||||
virtualize
|
virtualize
|
||||||
narrow
|
narrow
|
||||||
no-icon
|
no-icon
|
||||||
|
@ -21,7 +21,9 @@ export class HaLogbook extends LitElement {
|
|||||||
|
|
||||||
@property() public time!: { range: [Date, Date] } | { recent: number };
|
@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" })
|
@property({ type: Boolean, attribute: "narrow" })
|
||||||
public narrow = false;
|
public narrow = false;
|
||||||
@ -120,23 +122,47 @@ export class HaLogbook extends LitElement {
|
|||||||
|
|
||||||
this._lastLogbookDate = undefined;
|
this._lastLogbookDate = undefined;
|
||||||
this._logbookEntries = undefined;
|
this._logbookEntries = undefined;
|
||||||
this._error = undefined;
|
|
||||||
this._throttleGetLogbookEntries();
|
this._throttleGetLogbookEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues): void {
|
protected updated(changedProps: PropertyValues): void {
|
||||||
super.updated(changedProps);
|
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);
|
this.refresh(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._filterAlwaysEmptyResults) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We only need to fetch again if we track recent entries for an entity
|
// We only need to fetch again if we track recent entries for an entity
|
||||||
if (
|
if (
|
||||||
!("recent" in this.time) ||
|
!("recent" in this.time) ||
|
||||||
!changedProps.has("hass") ||
|
!changedProps.has("hass") ||
|
||||||
!this.entityId
|
!this.entityIds
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -146,7 +172,7 @@ export class HaLogbook extends LitElement {
|
|||||||
// Refresh data if we know the entity has changed.
|
// Refresh data if we know the entity has changed.
|
||||||
if (
|
if (
|
||||||
!oldHass ||
|
!oldHass ||
|
||||||
ensureArray(this.entityId).some(
|
ensureArray(this.entityIds).some(
|
||||||
(entityId) => this.hass.states[entityId] !== oldHass?.states[entityId]
|
(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() {
|
private async _getLogBookData() {
|
||||||
this._renderId += 1;
|
this._renderId += 1;
|
||||||
const renderId = this._renderId;
|
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 startTime: Date;
|
||||||
let endTime: Date;
|
let endTime: Date;
|
||||||
let appendData = false;
|
let appendData = false;
|
||||||
@ -173,27 +224,15 @@ export class HaLogbook extends LitElement {
|
|||||||
endTime = new Date();
|
endTime = new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
const entityIdFilter = this.entityId
|
|
||||||
? ensureArray(this.entityId)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
let newEntries: LogbookEntry[];
|
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 {
|
try {
|
||||||
newEntries = await getLogbookData(
|
newEntries = await getLogbookData(
|
||||||
this.hass,
|
this.hass,
|
||||||
startTime.toISOString(),
|
startTime.toISOString(),
|
||||||
endTime.toISOString(),
|
endTime.toISOString(),
|
||||||
entityIdFilter ? entityIdFilter.toString() : undefined
|
ensureArray(this.entityIds),
|
||||||
|
ensureArray(this.deviceIds)
|
||||||
);
|
);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (renderId === this._renderId) {
|
if (renderId === this._renderId) {
|
||||||
@ -201,13 +240,16 @@ export class HaLogbook extends LitElement {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// New render happening.
|
// New render happening.
|
||||||
if (renderId !== this._renderId) {
|
if (renderId !== this._renderId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put newest ones on top. Reverse works in-place so
|
||||||
|
// make a copy first.
|
||||||
|
newEntries = [...newEntries].reverse();
|
||||||
|
|
||||||
this._logbookEntries =
|
this._logbookEntries =
|
||||||
appendData && this._logbookEntries
|
appendData && this._logbookEntries
|
||||||
? newEntries.concat(...this._logbookEntries)
|
? newEntries.concat(...this._logbookEntries)
|
||||||
|
@ -38,7 +38,7 @@ export class HaPanelLogbook extends LitElement {
|
|||||||
|
|
||||||
@state() _time: { range: [Date, Date] };
|
@state() _time: { range: [Date, Date] };
|
||||||
|
|
||||||
@state() _entityId = "";
|
@state() _entityIds?: string[];
|
||||||
|
|
||||||
@property({ reflect: true, type: Boolean }) rtl = false;
|
@property({ reflect: true, type: Boolean }) rtl = false;
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ export class HaPanelLogbook extends LitElement {
|
|||||||
|
|
||||||
<ha-entity-picker
|
<ha-entity-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._entityId}
|
.value=${this._entityIds ? this._entityIds[0] : undefined}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.components.entity.entity-picker.entity"
|
"ui.components.entity.entity-picker.entity"
|
||||||
)}
|
)}
|
||||||
@ -97,7 +97,7 @@ export class HaPanelLogbook extends LitElement {
|
|||||||
<ha-logbook
|
<ha-logbook
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.time=${this._time}
|
.time=${this._time}
|
||||||
.entityId=${this._entityId}
|
.entityIds=${this._entityIds}
|
||||||
virtualize
|
virtualize
|
||||||
></ha-logbook>
|
></ha-logbook>
|
||||||
</ha-app-layout>
|
</ha-app-layout>
|
||||||
@ -157,15 +157,30 @@ export class HaPanelLogbook extends LitElement {
|
|||||||
this.rtl = computeRTL(this.hass);
|
this.rtl = computeRTL(this.hass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._applyURLParams();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _applyURLParams() {
|
private _applyURLParams() {
|
||||||
const searchParams = new URLSearchParams(location.search);
|
const searchParams = new URLSearchParams(location.search);
|
||||||
|
|
||||||
if (searchParams.has("entity_id")) {
|
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");
|
const startDateStr = searchParams.get("start_date");
|
||||||
@ -199,19 +214,19 @@ export class HaPanelLogbook extends LitElement {
|
|||||||
endDate.setDate(endDate.getDate() + 1);
|
endDate.setDate(endDate.getDate() + 1);
|
||||||
endDate.setMilliseconds(endDate.getMilliseconds() - 1);
|
endDate.setMilliseconds(endDate.getMilliseconds() - 1);
|
||||||
}
|
}
|
||||||
this._time = { range: [startDate, endDate] };
|
|
||||||
this._updatePath({
|
this._updatePath({
|
||||||
start_date: this._time.range[0].toISOString(),
|
start_date: startDate.toISOString(),
|
||||||
end_date: this._time.range[1].toISOString(),
|
end_date: endDate.toISOString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _entityPicked(ev) {
|
private _entityPicked(ev) {
|
||||||
this._entityId = ev.target.value;
|
this._updatePath({
|
||||||
this._updatePath({ entity_id: this._entityId });
|
entity_id: ev.target.value || undefined,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updatePath(update: Record<string, string>) {
|
private _updatePath(update: Record<string, string | undefined>) {
|
||||||
const params = extractSearchParamsObject();
|
const params = extractSearchParamsObject();
|
||||||
for (const [key, value] of Object.entries(update)) {
|
for (const [key, value] of Object.entries(update)) {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
|
@ -120,7 +120,7 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
|
|||||||
<ha-logbook
|
<ha-logbook
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.time=${this._time}
|
.time=${this._time}
|
||||||
.entityId=${this._entityId}
|
.entityIds=${this._entityId}
|
||||||
narrow
|
narrow
|
||||||
relative-time
|
relative-time
|
||||||
virtualize
|
virtualize
|
||||||
|
Loading…
x
Reference in New Issue
Block a user