mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Use statistics data in History graph card to fill gaps (#23612)
* Use statistics data in History graph card to fill gaps * add convertStatisticsToHistory utility
This commit is contained in:
parent
4b03caa01a
commit
707192feac
@ -45,7 +45,7 @@ declare global {
|
|||||||
export class StateHistoryCharts extends LitElement {
|
export class StateHistoryCharts extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public historyData!: HistoryResult;
|
@property({ attribute: false }) public historyData?: HistoryResult;
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow = false;
|
@property({ type: Boolean }) public narrow = false;
|
||||||
|
|
||||||
@ -119,12 +119,12 @@ export class StateHistoryCharts extends LitElement {
|
|||||||
${this.hass.localize("ui.components.history_charts.no_history_found")}
|
${this.hass.localize("ui.components.history_charts.no_history_found")}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
const combinedItems = this.historyData.timeline.length
|
const combinedItems = this.historyData!.timeline.length
|
||||||
? (this.virtualize
|
? (this.virtualize
|
||||||
? chunkData(this.historyData.timeline, CANVAS_TIMELINE_ROWS_CHUNK)
|
? chunkData(this.historyData!.timeline, CANVAS_TIMELINE_ROWS_CHUNK)
|
||||||
: [this.historyData.timeline]
|
: [this.historyData!.timeline]
|
||||||
).concat(this.historyData.line)
|
).concat(this.historyData!.line)
|
||||||
: this.historyData.line;
|
: this.historyData!.line;
|
||||||
|
|
||||||
// eslint-disable-next-line lit/no-this-assign-in-render
|
// eslint-disable-next-line lit/no-this-assign-in-render
|
||||||
this._chartCount = combinedItems.length;
|
this._chartCount = combinedItems.length;
|
||||||
|
@ -10,6 +10,7 @@ import { computeStateNameFromEntityAttributes } from "../common/entity/compute_s
|
|||||||
import type { LocalizeFunc } from "../common/translations/localize";
|
import type { LocalizeFunc } from "../common/translations/localize";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import type { FrontendLocaleData } from "./translation";
|
import type { FrontendLocaleData } from "./translation";
|
||||||
|
import type { Statistics } from "./recorder";
|
||||||
|
|
||||||
const DOMAINS_USE_LAST_UPDATED = ["climate", "humidifier", "water_heater"];
|
const DOMAINS_USE_LAST_UPDATED = ["climate", "humidifier", "water_heater"];
|
||||||
const NEED_ATTRIBUTE_DOMAINS = [
|
const NEED_ATTRIBUTE_DOMAINS = [
|
||||||
@ -417,6 +418,54 @@ const isNumericSensorEntity = (
|
|||||||
|
|
||||||
const BLANK_UNIT = " ";
|
const BLANK_UNIT = " ";
|
||||||
|
|
||||||
|
export const convertStatisticsToHistory = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
statistics: Statistics,
|
||||||
|
statisticIds: string[],
|
||||||
|
sensorNumericDeviceClasses: string[],
|
||||||
|
splitDeviceClasses = false
|
||||||
|
): HistoryResult => {
|
||||||
|
// Maintain the statistic id ordering
|
||||||
|
const orderedStatistics: Statistics = {};
|
||||||
|
statisticIds.forEach((id) => {
|
||||||
|
if (id in statistics) {
|
||||||
|
orderedStatistics[id] = statistics[id];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert statistics to HistoryResult format
|
||||||
|
const statsHistoryStates: HistoryStates = {};
|
||||||
|
Object.entries(orderedStatistics).forEach(([key, value]) => {
|
||||||
|
const entityHistoryStates: EntityHistoryState[] = value.map((e) => ({
|
||||||
|
s: e.mean != null ? e.mean.toString() : e.state!.toString(),
|
||||||
|
lc: e.start / 1000,
|
||||||
|
a: {},
|
||||||
|
lu: e.start / 1000,
|
||||||
|
}));
|
||||||
|
statsHistoryStates[key] = entityHistoryStates;
|
||||||
|
});
|
||||||
|
|
||||||
|
const statisticsHistory = computeHistory(
|
||||||
|
hass,
|
||||||
|
statsHistoryStates,
|
||||||
|
[],
|
||||||
|
hass.localize,
|
||||||
|
sensorNumericDeviceClasses,
|
||||||
|
splitDeviceClasses,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// remap states array to statistics array
|
||||||
|
(statisticsHistory?.line || []).forEach((item) => {
|
||||||
|
item.data.forEach((data) => {
|
||||||
|
data.statistics = data.states;
|
||||||
|
data.states = [];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return statisticsHistory;
|
||||||
|
};
|
||||||
|
|
||||||
export const computeHistory = (
|
export const computeHistory = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
stateHistory: HistoryStates,
|
stateHistory: HistoryStates,
|
||||||
@ -564,3 +613,101 @@ export const isNumericEntity = (
|
|||||||
domain === "sensor" &&
|
domain === "sensor" &&
|
||||||
isNumericSensorEntity(currentState, sensorNumericalDeviceClasses)) ||
|
isNumericSensorEntity(currentState, sensorNumericalDeviceClasses)) ||
|
||||||
numericStateFromHistory != null;
|
numericStateFromHistory != null;
|
||||||
|
|
||||||
|
export const mergeHistoryResults = (
|
||||||
|
historyResult: HistoryResult,
|
||||||
|
ltsResult?: HistoryResult,
|
||||||
|
splitDeviceClasses = true
|
||||||
|
): HistoryResult => {
|
||||||
|
if (!ltsResult) {
|
||||||
|
return historyResult;
|
||||||
|
}
|
||||||
|
const result: HistoryResult = { ...historyResult, line: [] };
|
||||||
|
|
||||||
|
const lookup: Record<
|
||||||
|
string,
|
||||||
|
{ historyItem?: LineChartUnit; ltsItem?: LineChartUnit }
|
||||||
|
> = {};
|
||||||
|
|
||||||
|
for (const item of historyResult.line) {
|
||||||
|
const key = computeGroupKey(
|
||||||
|
item.unit,
|
||||||
|
item.device_class,
|
||||||
|
splitDeviceClasses
|
||||||
|
);
|
||||||
|
if (key) {
|
||||||
|
lookup[key] = {
|
||||||
|
historyItem: item,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of ltsResult.line) {
|
||||||
|
const key = computeGroupKey(
|
||||||
|
item.unit,
|
||||||
|
item.device_class,
|
||||||
|
splitDeviceClasses
|
||||||
|
);
|
||||||
|
if (!key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (key in lookup) {
|
||||||
|
lookup[key].ltsItem = item;
|
||||||
|
} else {
|
||||||
|
lookup[key] = { ltsItem: item };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { historyItem, ltsItem } of Object.values(lookup)) {
|
||||||
|
if (!historyItem || !ltsItem) {
|
||||||
|
// Only one result has data for this item, so just push it directly instead of merging.
|
||||||
|
result.line.push(historyItem || ltsItem!);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newLineItem: LineChartUnit = { ...historyItem, data: [] };
|
||||||
|
const entities = new Set([
|
||||||
|
...historyItem.data.map((d) => d.entity_id),
|
||||||
|
...ltsItem.data.map((d) => d.entity_id),
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (const entity of entities) {
|
||||||
|
const historyDataItem = historyItem.data.find(
|
||||||
|
(d) => d.entity_id === entity
|
||||||
|
);
|
||||||
|
const ltsDataItem = ltsItem.data.find((d) => d.entity_id === entity);
|
||||||
|
|
||||||
|
if (!historyDataItem || !ltsDataItem) {
|
||||||
|
newLineItem.data.push(historyDataItem || ltsDataItem!);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove statistics that overlap with states
|
||||||
|
const oldestState =
|
||||||
|
historyDataItem.states[0]?.last_changed ||
|
||||||
|
// If no state, fall back to the max last changed of the last statistics (so approve all)
|
||||||
|
ltsDataItem.statistics![ltsDataItem.statistics!.length - 1]
|
||||||
|
.last_changed + 1;
|
||||||
|
|
||||||
|
const statistics: LineChartState[] = [];
|
||||||
|
for (const s of ltsDataItem.statistics!) {
|
||||||
|
if (s.last_changed >= oldestState) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
statistics.push(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
newLineItem.data.push(
|
||||||
|
statistics.length === 0
|
||||||
|
? // All statistics overlapped with states, so just push the states
|
||||||
|
historyDataItem
|
||||||
|
: {
|
||||||
|
...historyDataItem,
|
||||||
|
statistics,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
result.line.push(newLineItem);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
@ -36,19 +36,13 @@ import "../../components/ha-icon-button-arrow-prev";
|
|||||||
import "../../components/ha-menu-button";
|
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 type {
|
import type { HistoryResult } from "../../data/history";
|
||||||
EntityHistoryState,
|
|
||||||
HistoryResult,
|
|
||||||
HistoryStates,
|
|
||||||
LineChartState,
|
|
||||||
LineChartUnit,
|
|
||||||
} from "../../data/history";
|
|
||||||
import {
|
import {
|
||||||
computeGroupKey,
|
|
||||||
computeHistory,
|
computeHistory,
|
||||||
subscribeHistory,
|
subscribeHistory,
|
||||||
|
mergeHistoryResults,
|
||||||
|
convertStatisticsToHistory,
|
||||||
} from "../../data/history";
|
} from "../../data/history";
|
||||||
import type { Statistics } from "../../data/recorder";
|
|
||||||
import { fetchStatistics } from "../../data/recorder";
|
import { fetchStatistics } from "../../data/recorder";
|
||||||
import { resolveEntityIDs } from "../../data/selector";
|
import { resolveEntityIDs } from "../../data/selector";
|
||||||
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
||||||
@ -210,92 +204,6 @@ class HaPanelHistory extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _mergeHistoryResults(
|
|
||||||
ltsResult: HistoryResult,
|
|
||||||
historyResult: HistoryResult
|
|
||||||
): HistoryResult {
|
|
||||||
const result: HistoryResult = { ...historyResult, line: [] };
|
|
||||||
|
|
||||||
const lookup: Record<
|
|
||||||
string,
|
|
||||||
{ historyItem?: LineChartUnit; ltsItem?: LineChartUnit }
|
|
||||||
> = {};
|
|
||||||
|
|
||||||
for (const item of historyResult.line) {
|
|
||||||
const key = computeGroupKey(item.unit, item.device_class, true);
|
|
||||||
if (key) {
|
|
||||||
lookup[key] = {
|
|
||||||
historyItem: item,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const item of ltsResult.line) {
|
|
||||||
const key = computeGroupKey(item.unit, item.device_class, true);
|
|
||||||
if (!key) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (key in lookup) {
|
|
||||||
lookup[key].ltsItem = item;
|
|
||||||
} else {
|
|
||||||
lookup[key] = { ltsItem: item };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { historyItem, ltsItem } of Object.values(lookup)) {
|
|
||||||
if (!historyItem || !ltsItem) {
|
|
||||||
// Only one result has data for this item, so just push it directly instead of merging.
|
|
||||||
result.line.push(historyItem || ltsItem!);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newLineItem: LineChartUnit = { ...historyItem, data: [] };
|
|
||||||
const entities = new Set([
|
|
||||||
...historyItem.data.map((d) => d.entity_id),
|
|
||||||
...ltsItem.data.map((d) => d.entity_id),
|
|
||||||
]);
|
|
||||||
|
|
||||||
for (const entity of entities) {
|
|
||||||
const historyDataItem = historyItem.data.find(
|
|
||||||
(d) => d.entity_id === entity
|
|
||||||
);
|
|
||||||
const ltsDataItem = ltsItem.data.find((d) => d.entity_id === entity);
|
|
||||||
|
|
||||||
if (!historyDataItem || !ltsDataItem) {
|
|
||||||
newLineItem.data.push(historyDataItem || ltsDataItem!);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove statistics that overlap with states
|
|
||||||
const oldestState =
|
|
||||||
historyDataItem.states[0]?.last_changed ||
|
|
||||||
// If no state, fall back to the max last changed of the last statistics (so approve all)
|
|
||||||
ltsDataItem.statistics![ltsDataItem.statistics!.length - 1]
|
|
||||||
.last_changed + 1;
|
|
||||||
|
|
||||||
const statistics: LineChartState[] = [];
|
|
||||||
for (const s of ltsDataItem.statistics!) {
|
|
||||||
if (s.last_changed >= oldestState) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
statistics.push(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
newLineItem.data.push(
|
|
||||||
statistics.length === 0
|
|
||||||
? // All statistics overlapped with states, so just push the states
|
|
||||||
historyDataItem
|
|
||||||
: {
|
|
||||||
...historyDataItem,
|
|
||||||
statistics,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
result.line.push(newLineItem);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues) {
|
public willUpdate(changedProps: PropertyValues) {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
|
|
||||||
@ -307,9 +215,9 @@ class HaPanelHistory extends LitElement {
|
|||||||
changedProps.has("_targetPickerValue")
|
changedProps.has("_targetPickerValue")
|
||||||
) {
|
) {
|
||||||
if (this._statisticsHistory && this._stateHistory) {
|
if (this._statisticsHistory && this._stateHistory) {
|
||||||
this._mungedStateHistory = this._mergeHistoryResults(
|
this._mungedStateHistory = mergeHistoryResults(
|
||||||
this._statisticsHistory,
|
this._stateHistory,
|
||||||
this._stateHistory
|
this._statisticsHistory
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this._mungedStateHistory =
|
this._mungedStateHistory =
|
||||||
@ -410,45 +318,16 @@ class HaPanelHistory extends LitElement {
|
|||||||
["mean", "state"]
|
["mean", "state"]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Maintain the statistic id ordering
|
|
||||||
const orderedStatistics: Statistics = {};
|
|
||||||
statisticIds.forEach((id) => {
|
|
||||||
if (id in statistics) {
|
|
||||||
orderedStatistics[id] = statistics[id];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Convert statistics to HistoryResult format
|
|
||||||
const statsHistoryStates: HistoryStates = {};
|
|
||||||
Object.entries(orderedStatistics).forEach(([key, value]) => {
|
|
||||||
const entityHistoryStates: EntityHistoryState[] = value.map((e) => ({
|
|
||||||
s: e.mean != null ? e.mean.toString() : e.state!.toString(),
|
|
||||||
lc: e.start / 1000,
|
|
||||||
a: {},
|
|
||||||
lu: e.start / 1000,
|
|
||||||
}));
|
|
||||||
statsHistoryStates[key] = entityHistoryStates;
|
|
||||||
});
|
|
||||||
|
|
||||||
const { numeric_device_classes: sensorNumericDeviceClasses } =
|
const { numeric_device_classes: sensorNumericDeviceClasses } =
|
||||||
await getSensorNumericDeviceClasses(this.hass);
|
await getSensorNumericDeviceClasses(this.hass);
|
||||||
|
|
||||||
this._statisticsHistory = computeHistory(
|
this._statisticsHistory = convertStatisticsToHistory(
|
||||||
this.hass,
|
this.hass!,
|
||||||
statsHistoryStates,
|
statistics,
|
||||||
[],
|
statisticIds,
|
||||||
this.hass.localize,
|
|
||||||
sensorNumericDeviceClasses,
|
sensorNumericDeviceClasses,
|
||||||
true,
|
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
// remap states array to statistics array
|
|
||||||
(this._statisticsHistory?.line || []).forEach((item) => {
|
|
||||||
item.data.forEach((data) => {
|
|
||||||
data.statistics = data.states;
|
|
||||||
data.states = [];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _getHistory() {
|
private async _getHistory() {
|
||||||
|
@ -7,10 +7,12 @@ import "../../../components/chart/state-history-charts";
|
|||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
import type { HistoryResult } from "../../../data/history";
|
|
||||||
import {
|
import {
|
||||||
computeHistory,
|
computeHistory,
|
||||||
subscribeHistoryStatesTimeWindow,
|
subscribeHistoryStatesTimeWindow,
|
||||||
|
type HistoryResult,
|
||||||
|
convertStatisticsToHistory,
|
||||||
|
mergeHistoryResults,
|
||||||
} from "../../../data/history";
|
} from "../../../data/history";
|
||||||
import { getSensorNumericDeviceClasses } from "../../../data/sensor";
|
import { getSensorNumericDeviceClasses } from "../../../data/sensor";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
@ -19,6 +21,7 @@ import { processConfigEntities } from "../common/process-config-entities";
|
|||||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||||
import type { HistoryGraphCardConfig } from "./types";
|
import type { HistoryGraphCardConfig } from "./types";
|
||||||
import { createSearchParam } from "../../../common/url/search-params";
|
import { createSearchParam } from "../../../common/url/search-params";
|
||||||
|
import { fetchStatistics } from "../../../data/recorder";
|
||||||
|
|
||||||
export const DEFAULT_HOURS_TO_SHOW = 24;
|
export const DEFAULT_HOURS_TO_SHOW = 24;
|
||||||
|
|
||||||
@ -36,7 +39,9 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
|
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@state() private _stateHistory?: HistoryResult;
|
@state() private _history?: HistoryResult;
|
||||||
|
|
||||||
|
@state() private _statisticsHistory?: HistoryResult;
|
||||||
|
|
||||||
@state() private _config?: HistoryGraphCardConfig;
|
@state() private _config?: HistoryGraphCardConfig;
|
||||||
|
|
||||||
@ -118,7 +123,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._stateHistory = computeHistory(
|
const stateHistory = computeHistory(
|
||||||
this.hass!,
|
this.hass!,
|
||||||
combinedHistory,
|
combinedHistory,
|
||||||
this._entityIds,
|
this._entityIds,
|
||||||
@ -126,6 +131,12 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
sensorNumericDeviceClasses,
|
sensorNumericDeviceClasses,
|
||||||
this._config?.split_device_classes
|
this._config?.split_device_classes
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._history = mergeHistoryResults(
|
||||||
|
stateHistory,
|
||||||
|
this._statisticsHistory,
|
||||||
|
this._config?.split_device_classes
|
||||||
|
);
|
||||||
},
|
},
|
||||||
this._hoursToShow,
|
this._hoursToShow,
|
||||||
this._entityIds
|
this._entityIds
|
||||||
@ -133,12 +144,39 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
this._subscribed = undefined;
|
this._subscribed = undefined;
|
||||||
this._error = err;
|
this._error = err;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this._fetchStatistics(sensorNumericDeviceClasses);
|
||||||
|
|
||||||
this._setRedrawTimer();
|
this._setRedrawTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _fetchStatistics(sensorNumericDeviceClasses: string[]) {
|
||||||
|
const now = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setHours(start.getHours() - this._hoursToShow);
|
||||||
|
|
||||||
|
const statistics = await fetchStatistics(
|
||||||
|
this.hass!,
|
||||||
|
start,
|
||||||
|
now,
|
||||||
|
this._entityIds,
|
||||||
|
"hour",
|
||||||
|
undefined,
|
||||||
|
["mean", "state"]
|
||||||
|
);
|
||||||
|
|
||||||
|
this._statisticsHistory = convertStatisticsToHistory(
|
||||||
|
this.hass!,
|
||||||
|
statistics,
|
||||||
|
this._entityIds,
|
||||||
|
sensorNumericDeviceClasses,
|
||||||
|
this._config?.split_device_classes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private _redrawGraph() {
|
private _redrawGraph() {
|
||||||
if (this._stateHistory) {
|
if (this._history) {
|
||||||
this._stateHistory = { ...this._stateHistory };
|
this._history = { ...this._history };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,8 +267,8 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
: html`
|
: html`
|
||||||
<state-history-charts
|
<state-history-charts
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.isLoadingData=${!this._stateHistory}
|
.isLoadingData=${!this._history}
|
||||||
.historyData=${this._stateHistory}
|
.historyData=${this._history}
|
||||||
.names=${this._names}
|
.names=${this._names}
|
||||||
up-to-now
|
up-to-now
|
||||||
.hoursToShow=${this._hoursToShow}
|
.hoursToShow=${this._hoursToShow}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user