|
|
@@ -9,7 +9,6 @@ import { property, query, state } from "lit/decorators";
|
|
|
|
import memoizeOne from "memoize-one";
|
|
|
|
import memoizeOne from "memoize-one";
|
|
|
|
import { ensureArray } from "../../common/array/ensure-array";
|
|
|
|
import { ensureArray } from "../../common/array/ensure-array";
|
|
|
|
import { storage } from "../../common/decorators/storage";
|
|
|
|
import { storage } from "../../common/decorators/storage";
|
|
|
|
import { computeDomain } from "../../common/entity/compute_domain";
|
|
|
|
|
|
|
|
import { navigate } from "../../common/navigate";
|
|
|
|
import { navigate } from "../../common/navigate";
|
|
|
|
import { constructUrlCurrentPath } from "../../common/url/construct-url";
|
|
|
|
import { constructUrlCurrentPath } from "../../common/url/construct-url";
|
|
|
|
import {
|
|
|
|
import {
|
|
|
@@ -28,29 +27,37 @@ import "../../components/ha-menu-button";
|
|
|
|
import "../../components/ha-target-picker";
|
|
|
|
import "../../components/ha-target-picker";
|
|
|
|
import "../../components/ha-top-app-bar-fixed";
|
|
|
|
import "../../components/ha-top-app-bar-fixed";
|
|
|
|
import {
|
|
|
|
import {
|
|
|
|
EntityHistoryState,
|
|
|
|
AreaDeviceLookup,
|
|
|
|
|
|
|
|
AreaEntityLookup,
|
|
|
|
|
|
|
|
getAreaDeviceLookup,
|
|
|
|
|
|
|
|
getAreaEntityLookup,
|
|
|
|
|
|
|
|
} from "../../data/area_registry";
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
|
|
|
DeviceEntityLookup,
|
|
|
|
|
|
|
|
getDeviceEntityLookup,
|
|
|
|
|
|
|
|
subscribeDeviceRegistry,
|
|
|
|
|
|
|
|
} from "../../data/device_registry";
|
|
|
|
|
|
|
|
import { subscribeEntityRegistry } from "../../data/entity_registry";
|
|
|
|
|
|
|
|
import {
|
|
|
|
HistoryResult,
|
|
|
|
HistoryResult,
|
|
|
|
HistoryStates,
|
|
|
|
|
|
|
|
LineChartState,
|
|
|
|
|
|
|
|
LineChartUnit,
|
|
|
|
|
|
|
|
computeGroupKey,
|
|
|
|
|
|
|
|
computeHistory,
|
|
|
|
computeHistory,
|
|
|
|
subscribeHistory,
|
|
|
|
subscribeHistory,
|
|
|
|
|
|
|
|
HistoryStates,
|
|
|
|
|
|
|
|
EntityHistoryState,
|
|
|
|
|
|
|
|
LineChartUnit,
|
|
|
|
|
|
|
|
computeGroupKey,
|
|
|
|
|
|
|
|
LineChartState,
|
|
|
|
} from "../../data/history";
|
|
|
|
} from "../../data/history";
|
|
|
|
import { Statistics, fetchStatistics } from "../../data/recorder";
|
|
|
|
import { fetchStatistics, Statistics } from "../../data/recorder";
|
|
|
|
import {
|
|
|
|
|
|
|
|
expandAreaTarget,
|
|
|
|
|
|
|
|
expandDeviceTarget,
|
|
|
|
|
|
|
|
expandFloorTarget,
|
|
|
|
|
|
|
|
expandLabelTarget,
|
|
|
|
|
|
|
|
} from "../../data/selector";
|
|
|
|
|
|
|
|
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
|
|
|
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
|
|
|
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
|
|
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
|
|
|
import { haStyle } from "../../resources/styles";
|
|
|
|
import { haStyle } from "../../resources/styles";
|
|
|
|
import { HomeAssistant } from "../../types";
|
|
|
|
import { HomeAssistant } from "../../types";
|
|
|
|
import { fileDownload } from "../../util/file_download";
|
|
|
|
import { fileDownload } from "../../util/file_download";
|
|
|
|
|
|
|
|
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
|
|
|
|
|
|
|
|
import { computeDomain } from "../../common/entity/compute_domain";
|
|
|
|
|
|
|
|
|
|
|
|
class HaPanelHistory extends LitElement {
|
|
|
|
class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|
|
|
@property({ attribute: false }) hass!: HomeAssistant;
|
|
|
|
@property({ attribute: false }) hass!: HomeAssistant;
|
|
|
|
|
|
|
|
|
|
|
|
@property({ reflect: true, type: Boolean }) public narrow = false;
|
|
|
|
@property({ reflect: true, type: Boolean }) public narrow = false;
|
|
|
@@ -76,6 +83,12 @@ class HaPanelHistory extends LitElement {
|
|
|
|
|
|
|
|
|
|
|
|
@state() private _statisticsHistory?: HistoryResult;
|
|
|
|
@state() private _statisticsHistory?: HistoryResult;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@state() private _deviceEntityLookup?: DeviceEntityLookup;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@state() private _areaEntityLookup?: AreaEntityLookup;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@state() private _areaDeviceLookup?: AreaDeviceLookup;
|
|
|
|
|
|
|
|
|
|
|
|
@state()
|
|
|
|
@state()
|
|
|
|
private _showBack?: boolean;
|
|
|
|
private _showBack?: boolean;
|
|
|
|
|
|
|
|
|
|
|
@@ -110,6 +123,18 @@ class HaPanelHistory extends LitElement {
|
|
|
|
this._unsubscribeHistory();
|
|
|
|
this._unsubscribeHistory();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public hassSubscribe(): UnsubscribeFunc[] {
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
|
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
|
|
|
|
|
|
|
this._deviceEntityLookup = getDeviceEntityLookup(entities);
|
|
|
|
|
|
|
|
this._areaEntityLookup = getAreaEntityLookup(entities);
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
|
|
|
|
|
|
|
this._areaDeviceLookup = getAreaDeviceLookup(devices);
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private _goBack(): void {
|
|
|
|
private _goBack(): void {
|
|
|
|
history.back();
|
|
|
|
history.back();
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -307,9 +332,7 @@ class HaPanelHistory extends LitElement {
|
|
|
|
const entityIds = searchParams.entity_id;
|
|
|
|
const entityIds = searchParams.entity_id;
|
|
|
|
const deviceIds = searchParams.device_id;
|
|
|
|
const deviceIds = searchParams.device_id;
|
|
|
|
const areaIds = searchParams.area_id;
|
|
|
|
const areaIds = searchParams.area_id;
|
|
|
|
const floorIds = searchParams.floor_id;
|
|
|
|
if (entityIds || deviceIds || areaIds) {
|
|
|
|
const labelsIds = searchParams.label_id;
|
|
|
|
|
|
|
|
if (entityIds || deviceIds || areaIds || floorIds || labelsIds) {
|
|
|
|
|
|
|
|
this._targetPickerValue = {};
|
|
|
|
this._targetPickerValue = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (entityIds) {
|
|
|
|
if (entityIds) {
|
|
|
@@ -324,14 +347,6 @@ class HaPanelHistory extends LitElement {
|
|
|
|
const splitIds = areaIds.split(",");
|
|
|
|
const splitIds = areaIds.split(",");
|
|
|
|
this._targetPickerValue!.area_id = splitIds;
|
|
|
|
this._targetPickerValue!.area_id = splitIds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (floorIds) {
|
|
|
|
|
|
|
|
const splitIds = floorIds.split(",");
|
|
|
|
|
|
|
|
this._targetPickerValue!.floor_id = splitIds;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (labelsIds) {
|
|
|
|
|
|
|
|
const splitIds = labelsIds.split(",");
|
|
|
|
|
|
|
|
this._targetPickerValue!.label_id = splitIds;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const startDate = searchParams.start_date;
|
|
|
|
const startDate = searchParams.start_date;
|
|
|
|
if (startDate) {
|
|
|
|
if (startDate) {
|
|
|
@@ -507,77 +522,95 @@ class HaPanelHistory extends LitElement {
|
|
|
|
private _getEntityIds(): string[] {
|
|
|
|
private _getEntityIds(): string[] {
|
|
|
|
return this.__getEntityIds(
|
|
|
|
return this.__getEntityIds(
|
|
|
|
this._targetPickerValue,
|
|
|
|
this._targetPickerValue,
|
|
|
|
this.hass.entities,
|
|
|
|
this._deviceEntityLookup,
|
|
|
|
this.hass.devices,
|
|
|
|
this._areaEntityLookup,
|
|
|
|
this.hass.areas
|
|
|
|
this._areaDeviceLookup
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private __getEntityIds = memoizeOne(
|
|
|
|
private __getEntityIds = memoizeOne(
|
|
|
|
(
|
|
|
|
(
|
|
|
|
targetPickerValue: HassServiceTarget,
|
|
|
|
targetPickerValue: HassServiceTarget,
|
|
|
|
entities: HomeAssistant["entities"],
|
|
|
|
deviceEntityLookup: DeviceEntityLookup | undefined,
|
|
|
|
devices: HomeAssistant["devices"],
|
|
|
|
areaEntityLookup: AreaEntityLookup | undefined,
|
|
|
|
areas: HomeAssistant["areas"]
|
|
|
|
areaDeviceLookup: AreaDeviceLookup | undefined
|
|
|
|
): string[] => {
|
|
|
|
): string[] => {
|
|
|
|
if (!targetPickerValue) {
|
|
|
|
if (
|
|
|
|
|
|
|
|
!targetPickerValue ||
|
|
|
|
|
|
|
|
deviceEntityLookup === undefined ||
|
|
|
|
|
|
|
|
areaEntityLookup === undefined ||
|
|
|
|
|
|
|
|
areaDeviceLookup === undefined
|
|
|
|
|
|
|
|
) {
|
|
|
|
return [];
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const targetSelector = { target: {} };
|
|
|
|
const entityIds = new Set<string>();
|
|
|
|
const targetEntities = new Set(ensureArray(targetPickerValue.entity_id));
|
|
|
|
let {
|
|
|
|
const targetDevices = new Set(ensureArray(targetPickerValue.device_id));
|
|
|
|
area_id: searchingAreaId,
|
|
|
|
const targetAreas = new Set(ensureArray(targetPickerValue.area_id));
|
|
|
|
device_id: searchingDeviceId,
|
|
|
|
const targetFloors = new Set(ensureArray(targetPickerValue.floor_id));
|
|
|
|
entity_id: searchingEntityId,
|
|
|
|
const targetLabels = new Set(ensureArray(targetPickerValue.label_id));
|
|
|
|
} = targetPickerValue;
|
|
|
|
|
|
|
|
|
|
|
|
targetLabels.forEach((labelId) => {
|
|
|
|
if (searchingAreaId) {
|
|
|
|
const expanded = expandLabelTarget(
|
|
|
|
searchingAreaId = ensureArray(searchingAreaId);
|
|
|
|
this.hass,
|
|
|
|
for (const singleSearchingAreaId of searchingAreaId) {
|
|
|
|
labelId,
|
|
|
|
const foundEntities = areaEntityLookup[singleSearchingAreaId];
|
|
|
|
areas,
|
|
|
|
if (foundEntities?.length) {
|
|
|
|
devices,
|
|
|
|
for (const foundEntity of foundEntities) {
|
|
|
|
entities,
|
|
|
|
if (foundEntity.entity_category === null) {
|
|
|
|
targetSelector
|
|
|
|
entityIds.add(foundEntity.entity_id);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
expanded.devices.forEach((id) => targetDevices.add(id));
|
|
|
|
}
|
|
|
|
expanded.entities.forEach((id) => targetEntities.add(id));
|
|
|
|
}
|
|
|
|
expanded.areas.forEach((id) => targetAreas.add(id));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
targetFloors.forEach((floorId) => {
|
|
|
|
const foundDevices = areaDeviceLookup[singleSearchingAreaId];
|
|
|
|
const expanded = expandFloorTarget(
|
|
|
|
if (!foundDevices?.length) {
|
|
|
|
this.hass,
|
|
|
|
continue;
|
|
|
|
floorId,
|
|
|
|
}
|
|
|
|
areas,
|
|
|
|
|
|
|
|
targetSelector
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
expanded.areas.forEach((id) => targetAreas.add(id));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
targetAreas.forEach((areaId) => {
|
|
|
|
for (const foundDevice of foundDevices) {
|
|
|
|
const expanded = expandAreaTarget(
|
|
|
|
const foundDeviceEntities = deviceEntityLookup[foundDevice.id];
|
|
|
|
this.hass,
|
|
|
|
if (!foundDeviceEntities?.length) {
|
|
|
|
areaId,
|
|
|
|
continue;
|
|
|
|
devices,
|
|
|
|
}
|
|
|
|
entities,
|
|
|
|
|
|
|
|
targetSelector
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
expanded.devices.forEach((id) => targetDevices.add(id));
|
|
|
|
|
|
|
|
expanded.entities.forEach((id) => targetEntities.add(id));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
targetDevices.forEach((deviceId) => {
|
|
|
|
for (const foundDeviceEntity of foundDeviceEntities) {
|
|
|
|
const expanded = expandDeviceTarget(
|
|
|
|
if (
|
|
|
|
this.hass,
|
|
|
|
(!foundDeviceEntity.area_id ||
|
|
|
|
deviceId,
|
|
|
|
foundDeviceEntity.area_id === singleSearchingAreaId) &&
|
|
|
|
entities,
|
|
|
|
foundDeviceEntity.entity_category === null
|
|
|
|
targetSelector
|
|
|
|
) {
|
|
|
|
);
|
|
|
|
entityIds.add(foundDeviceEntity.entity_id);
|
|
|
|
expanded.entities.forEach((id) => targetEntities.add(id));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Array.from(targetEntities);
|
|
|
|
if (searchingDeviceId) {
|
|
|
|
|
|
|
|
searchingDeviceId = ensureArray(searchingDeviceId);
|
|
|
|
|
|
|
|
for (const singleSearchingDeviceId of searchingDeviceId) {
|
|
|
|
|
|
|
|
const foundEntities = deviceEntityLookup[singleSearchingDeviceId];
|
|
|
|
|
|
|
|
if (!foundEntities?.length) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const foundEntity of foundEntities) {
|
|
|
|
|
|
|
|
if (foundEntity.entity_category === null) {
|
|
|
|
|
|
|
|
entityIds.add(foundEntity.entity_id);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (searchingEntityId) {
|
|
|
|
|
|
|
|
searchingEntityId = ensureArray(searchingEntityId);
|
|
|
|
|
|
|
|
for (const singleSearchingEntityId of searchingEntityId) {
|
|
|
|
|
|
|
|
entityIds.add(singleSearchingEntityId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return [...entityIds];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
@@ -606,12 +639,6 @@ class HaPanelHistory extends LitElement {
|
|
|
|
","
|
|
|
|
","
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this._targetPickerValue.label_id) {
|
|
|
|
|
|
|
|
params.label_id = ensureArray(this._targetPickerValue.label_id).join(",");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._targetPickerValue.floor_id) {
|
|
|
|
|
|
|
|
params.floor_id = ensureArray(this._targetPickerValue.floor_id).join(",");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._targetPickerValue.area_id) {
|
|
|
|
if (this._targetPickerValue.area_id) {
|
|
|
|
params.area_id = ensureArray(this._targetPickerValue.area_id).join(",");
|
|
|
|
params.area_id = ensureArray(this._targetPickerValue.area_id).join(",");
|
|
|
|
}
|
|
|
|
}
|
|
|
|