mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-05 23:47:54 +00:00
Add support for streaming history
This commit is contained in:
parent
412587a457
commit
c34e845886
@ -187,9 +187,8 @@ export const subscribeHistory = (
|
||||
endTime: Date,
|
||||
entityIds: string[]
|
||||
): Promise<() => Promise<void>> => {
|
||||
// If all specified filters are empty lists, we can return an empty list.
|
||||
const params = {
|
||||
type: "history/history_during_period",
|
||||
type: "history/stream",
|
||||
start_time: startTime.toISOString(),
|
||||
end_time: endTime.toISOString(),
|
||||
minimal_response: true,
|
||||
@ -206,16 +205,13 @@ export const subscribeHistory = (
|
||||
class HistoryStream {
|
||||
hass: HomeAssistant;
|
||||
|
||||
startTime: Date;
|
||||
|
||||
endTime: Date;
|
||||
hoursToShow: number;
|
||||
|
||||
combinedHistory: HistoryStates;
|
||||
|
||||
constructor(hass: HomeAssistant, startTime: Date, endTime: Date) {
|
||||
constructor(hass: HomeAssistant, hoursToShow: number) {
|
||||
this.hass = hass;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.hoursToShow = hoursToShow;
|
||||
this.combinedHistory = {};
|
||||
}
|
||||
|
||||
@ -231,7 +227,8 @@ class HistoryStream {
|
||||
// indicate no more historical events
|
||||
return this.combinedHistory;
|
||||
}
|
||||
const purgeBeforePythonTime = this.startTime.getTime() / 1000;
|
||||
const purgeBeforePythonTime =
|
||||
(new Date().getTime() - 60 * 60 * this.hoursToShow * 1000) / 1000;
|
||||
const newHistory: HistoryStates = {};
|
||||
Object.keys(streamMessage.states).forEach((entityId) => {
|
||||
newHistory[entityId] = [];
|
||||
@ -260,24 +257,23 @@ class HistoryStream {
|
||||
}
|
||||
}
|
||||
|
||||
export const subscribeHistoryStates = (
|
||||
export const subscribeHistoryStatesWindow = (
|
||||
hass: HomeAssistant,
|
||||
callbackFunction: (data: HistoryStates) => void,
|
||||
startTime: Date,
|
||||
endTime: Date,
|
||||
hoursToShow: number,
|
||||
entityIds: string[]
|
||||
): Promise<() => Promise<void>> => {
|
||||
// If all specified filters are empty lists, we can return an empty list.
|
||||
const params = {
|
||||
type: "history/history_during_period",
|
||||
start_time: startTime.toISOString(),
|
||||
end_time: endTime.toISOString(),
|
||||
type: "history/stream",
|
||||
start_time: new Date(
|
||||
new Date().getTime() - 60 * 60 * hoursToShow * 1000
|
||||
).toISOString(),
|
||||
minimal_response: true,
|
||||
no_attributes: !entityIds.some((entityId) =>
|
||||
entityIdHistoryNeedsAttributes(hass, entityId)
|
||||
),
|
||||
};
|
||||
const stream = new HistoryStream(hass, startTime, endTime);
|
||||
const stream = new HistoryStream(hass, hoursToShow);
|
||||
return hass.connection.subscribeMessage<HistoryStreamMessage>(
|
||||
(message) => callbackFunction(stream.processMessage(message)),
|
||||
params
|
||||
|
@ -8,11 +8,13 @@ import {
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { throttle } from "../../../common/util/throttle";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/chart/state-history-charts";
|
||||
import { CacheConfig, getRecentWithCache } from "../../../data/cached-history";
|
||||
import { HistoryResult } from "../../../data/history";
|
||||
import {
|
||||
HistoryResult,
|
||||
subscribeHistoryStatesWindow,
|
||||
computeHistory,
|
||||
} from "../../../data/history";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
|
||||
import { processConfigEntities } from "../common/process-config-entities";
|
||||
@ -42,11 +44,13 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
|
||||
private _names: Record<string, string> = {};
|
||||
|
||||
private _cacheConfig?: CacheConfig;
|
||||
private _entityIds: string[] = [];
|
||||
|
||||
private _fetching = false;
|
||||
private _hoursToShow = 24;
|
||||
|
||||
private _throttleGetStateHistory?: () => void;
|
||||
private _error?: string;
|
||||
|
||||
private _subscribed?: Promise<(() => Promise<void>) | void>;
|
||||
|
||||
public getCardSize(): number {
|
||||
return this._config?.title
|
||||
@ -67,27 +71,71 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
? processConfigEntities(config.entities)
|
||||
: [];
|
||||
|
||||
const _entities: string[] = [];
|
||||
|
||||
this._configEntities.forEach((entity) => {
|
||||
_entities.push(entity.entity);
|
||||
this._entityIds.push(entity.entity);
|
||||
if (entity.name) {
|
||||
this._names[entity.entity] = entity.name;
|
||||
}
|
||||
});
|
||||
|
||||
this._throttleGetStateHistory = throttle(() => {
|
||||
this._getStateHistory();
|
||||
}, config.refresh_interval || 10 * 1000);
|
||||
|
||||
this._cacheConfig = {
|
||||
cacheKey: _entities.join(),
|
||||
hoursToShow: config.hours_to_show || 24,
|
||||
};
|
||||
this._hoursToShow = config.hours_to_show || 24;
|
||||
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.hasUpdated) {
|
||||
this._subscribeHistoryWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unsubscribeHistoryWindow();
|
||||
}
|
||||
|
||||
private _subscribeHistoryWindow() {
|
||||
if (this._subscribed) {
|
||||
return true;
|
||||
}
|
||||
this._subscribed = subscribeHistoryStatesWindow(
|
||||
this.hass!,
|
||||
(combinedHistory) => {
|
||||
// "recent" means start time is a sliding window
|
||||
// so we need to calculate an expireTime to
|
||||
// purge old events
|
||||
if (!this._subscribed) {
|
||||
// Message came in before we had a chance to unload
|
||||
return;
|
||||
}
|
||||
this._stateHistory = computeHistory(
|
||||
this.hass!,
|
||||
combinedHistory,
|
||||
this.hass!.localize
|
||||
);
|
||||
},
|
||||
this._hoursToShow,
|
||||
this._entityIds
|
||||
).catch((err) => {
|
||||
this._subscribed = undefined;
|
||||
this._error = err;
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private _unsubscribeHistoryWindow() {
|
||||
if (!this._subscribed) {
|
||||
return;
|
||||
}
|
||||
this._subscribed.then((unsubscribe) => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
this._subscribed = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
if (changedProps.has("_stateHistory")) {
|
||||
return true;
|
||||
@ -100,8 +148,8 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
!this._throttleGetStateHistory ||
|
||||
!this._cacheConfig
|
||||
!this._hoursToShow ||
|
||||
!this._entityIds.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@ -117,12 +165,10 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
if (
|
||||
changedProps.has("_config") &&
|
||||
(oldConfig?.entities !== this._config.entities ||
|
||||
oldConfig?.hours_to_show !== this._config.hours_to_show)
|
||||
oldConfig?.hours_to_show !== this._hoursToShow)
|
||||
) {
|
||||
this._throttleGetStateHistory();
|
||||
} else if (changedProps.has("hass")) {
|
||||
// wait for commit of data (we only account for the default setting of 1 sec)
|
||||
setTimeout(this._throttleGetStateHistory, 1000);
|
||||
this._unsubscribeHistoryWindow();
|
||||
this._subscribeHistoryWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,6 +177,10 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
return html``;
|
||||
}
|
||||
|
||||
if (this._error) {
|
||||
return html`<div class="errors">${this._error}</div>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-card .header=${this._config.title}>
|
||||
<div
|
||||
@ -153,26 +203,6 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _getStateHistory(): Promise<void> {
|
||||
if (this._fetching) {
|
||||
return;
|
||||
}
|
||||
this._fetching = true;
|
||||
try {
|
||||
this._stateHistory = {
|
||||
...(await getRecentWithCache(
|
||||
this.hass!,
|
||||
this._configEntities!.map((config) => config.entity),
|
||||
this._cacheConfig!,
|
||||
this.hass!.localize,
|
||||
this.hass!.language
|
||||
)),
|
||||
};
|
||||
} finally {
|
||||
this._fetching = false;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-card {
|
||||
|
Loading…
x
Reference in New Issue
Block a user