mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 18:56:39 +00:00
Add streaming to history panel (#15301)
This commit is contained in:
parent
57289b0bbe
commit
4bce4152d3
@ -64,6 +64,8 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
changedProps.has("startTime") ||
|
||||||
|
changedProps.has("endTime") ||
|
||||||
changedProps.has("data") ||
|
changedProps.has("data") ||
|
||||||
this._chartTime <
|
this._chartTime <
|
||||||
new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES)
|
new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES)
|
||||||
|
@ -38,7 +38,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@customElement("state-history-charts")
|
@customElement("state-history-charts")
|
||||||
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;
|
||||||
@ -71,7 +71,6 @@ class StateHistoryCharts extends LitElement {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@restoreScroll(".container") private _savedScrollPos?: number;
|
@restoreScroll(".container") private _savedScrollPos?: number;
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!isComponentLoaded(this.hass, "history")) {
|
if (!isComponentLoaded(this.hass, "history")) {
|
||||||
return html`<div class="info">
|
return html`<div class="info">
|
||||||
|
@ -117,7 +117,7 @@ export const fetchDateWS = (
|
|||||||
|
|
||||||
export const subscribeHistory = (
|
export const subscribeHistory = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
callbackFunction: (message: HistoryStreamMessage) => void,
|
callbackFunction: (data: HistoryStates) => void,
|
||||||
startTime: Date,
|
startTime: Date,
|
||||||
endTime: Date,
|
endTime: Date,
|
||||||
entityIds: string[]
|
entityIds: string[]
|
||||||
@ -132,8 +132,9 @@ export const subscribeHistory = (
|
|||||||
entityIdHistoryNeedsAttributes(hass, entityId)
|
entityIdHistoryNeedsAttributes(hass, entityId)
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
const stream = new HistoryStream(hass);
|
||||||
return hass.connection.subscribeMessage<HistoryStreamMessage>(
|
return hass.connection.subscribeMessage<HistoryStreamMessage>(
|
||||||
(message) => callbackFunction(message),
|
(message) => callbackFunction(stream.processMessage(message)),
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -141,11 +142,11 @@ export const subscribeHistory = (
|
|||||||
class HistoryStream {
|
class HistoryStream {
|
||||||
hass: HomeAssistant;
|
hass: HomeAssistant;
|
||||||
|
|
||||||
hoursToShow: number;
|
hoursToShow?: number;
|
||||||
|
|
||||||
combinedHistory: HistoryStates;
|
combinedHistory: HistoryStates;
|
||||||
|
|
||||||
constructor(hass: HomeAssistant, hoursToShow: number) {
|
constructor(hass: HomeAssistant, hoursToShow?: number) {
|
||||||
this.hass = hass;
|
this.hass = hass;
|
||||||
this.hoursToShow = hoursToShow;
|
this.hoursToShow = hoursToShow;
|
||||||
this.combinedHistory = {};
|
this.combinedHistory = {};
|
||||||
@ -161,8 +162,9 @@ class HistoryStream {
|
|||||||
// indicate no more historical events
|
// indicate no more historical events
|
||||||
return this.combinedHistory;
|
return this.combinedHistory;
|
||||||
}
|
}
|
||||||
const purgeBeforePythonTime =
|
const purgeBeforePythonTime = this.hoursToShow
|
||||||
(new Date().getTime() - 60 * 60 * this.hoursToShow * 1000) / 1000;
|
? (new Date().getTime() - 60 * 60 * this.hoursToShow * 1000) / 1000
|
||||||
|
: undefined;
|
||||||
const newHistory: HistoryStates = {};
|
const newHistory: HistoryStates = {};
|
||||||
for (const entityId of Object.keys(this.combinedHistory)) {
|
for (const entityId of Object.keys(this.combinedHistory)) {
|
||||||
newHistory[entityId] = [];
|
newHistory[entityId] = [];
|
||||||
@ -195,7 +197,7 @@ class HistoryStream {
|
|||||||
newHistory[entityId] = streamMessage.states[entityId];
|
newHistory[entityId] = streamMessage.states[entityId];
|
||||||
}
|
}
|
||||||
// Remove old history
|
// Remove old history
|
||||||
if (entityId in this.combinedHistory) {
|
if (purgeBeforePythonTime && entityId in this.combinedHistory) {
|
||||||
const expiredStates = newHistory[entityId].filter(
|
const expiredStates = newHistory[entityId].filter(
|
||||||
(state) => state.lu < purgeBeforePythonTime
|
(state) => state.lu < purgeBeforePythonTime
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,7 @@ import "@polymer/app-layout/app-header/app-header";
|
|||||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||||
import {
|
import {
|
||||||
addDays,
|
addDays,
|
||||||
|
differenceInHours,
|
||||||
endOfToday,
|
endOfToday,
|
||||||
endOfWeek,
|
endOfWeek,
|
||||||
endOfYesterday,
|
endOfYesterday,
|
||||||
@ -15,17 +16,19 @@ import {
|
|||||||
UnsubscribeFunc,
|
UnsubscribeFunc,
|
||||||
} from "home-assistant-js-websocket/dist/types";
|
} from "home-assistant-js-websocket/dist/types";
|
||||||
import { css, html, LitElement, PropertyValues } from "lit";
|
import { css, html, LitElement, PropertyValues } from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, query, state } from "lit/decorators";
|
||||||
|
import { ensureArray } from "../../common/array/ensure-array";
|
||||||
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
|
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
|
||||||
import { LocalStorage } from "../../common/decorators/local-storage";
|
import { LocalStorage } from "../../common/decorators/local-storage";
|
||||||
import { ensureArray } from "../../common/array/ensure-array";
|
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import {
|
import {
|
||||||
createSearchParam,
|
createSearchParam,
|
||||||
extractSearchParamsObject,
|
extractSearchParamsObject,
|
||||||
} from "../../common/url/search-params";
|
} from "../../common/url/search-params";
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
|
import { MIN_TIME_BETWEEN_UPDATES } from "../../components/chart/ha-chart-base";
|
||||||
import "../../components/chart/state-history-charts";
|
import "../../components/chart/state-history-charts";
|
||||||
|
import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
|
||||||
import "../../components/ha-circular-progress";
|
import "../../components/ha-circular-progress";
|
||||||
import "../../components/ha-date-range-picker";
|
import "../../components/ha-date-range-picker";
|
||||||
import type { DateRangePickerRanges } from "../../components/ha-date-range-picker";
|
import type { DateRangePickerRanges } from "../../components/ha-date-range-picker";
|
||||||
@ -44,7 +47,11 @@ import {
|
|||||||
subscribeDeviceRegistry,
|
subscribeDeviceRegistry,
|
||||||
} from "../../data/device_registry";
|
} from "../../data/device_registry";
|
||||||
import { subscribeEntityRegistry } from "../../data/entity_registry";
|
import { subscribeEntityRegistry } from "../../data/entity_registry";
|
||||||
import { computeHistory, fetchDateWS } from "../../data/history";
|
import {
|
||||||
|
computeHistory,
|
||||||
|
HistoryResult,
|
||||||
|
subscribeHistory,
|
||||||
|
} from "../../data/history";
|
||||||
import "../../layouts/ha-app-layout";
|
import "../../layouts/ha-app-layout";
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
import { haStyle } from "../../resources/styles";
|
import { haStyle } from "../../resources/styles";
|
||||||
@ -66,7 +73,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _isLoading = false;
|
@state() private _isLoading = false;
|
||||||
|
|
||||||
@state() private _stateHistory?;
|
@state() private _stateHistory?: HistoryResult;
|
||||||
|
|
||||||
@state() private _ranges?: DateRangePickerRanges;
|
@state() private _ranges?: DateRangePickerRanges;
|
||||||
|
|
||||||
@ -76,18 +83,37 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _areaDeviceLookup?: AreaDeviceLookup;
|
@state() private _areaDeviceLookup?: AreaDeviceLookup;
|
||||||
|
|
||||||
|
@query("state-history-charts")
|
||||||
|
private _stateHistoryCharts?: StateHistoryCharts;
|
||||||
|
|
||||||
|
private _subscribed?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
|
private _interval?: number;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
const start = new Date();
|
const start = new Date();
|
||||||
start.setHours(start.getHours() - 2, 0, 0, 0);
|
start.setHours(start.getHours() - 1, 0, 0, 0);
|
||||||
this._startDate = start;
|
this._startDate = start;
|
||||||
|
|
||||||
const end = new Date();
|
const end = new Date();
|
||||||
end.setHours(end.getHours() + 1, 0, 0, 0);
|
end.setHours(end.getHours() + 2, 0, 0, 0);
|
||||||
this._endDate = end;
|
this._endDate = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
if (this.hasUpdated) {
|
||||||
|
this._getHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._unsubscribeHistory();
|
||||||
|
}
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
@ -270,24 +296,63 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
if (entityIds.length === 0) {
|
if (entityIds.length === 0) {
|
||||||
this._isLoading = false;
|
this._isLoading = false;
|
||||||
this._stateHistory = [];
|
this._stateHistory = { line: [], timeline: [] };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const dateHistory = await fetchDateWS(
|
|
||||||
this.hass,
|
|
||||||
this._startDate,
|
|
||||||
this._endDate,
|
|
||||||
entityIds
|
|
||||||
);
|
|
||||||
|
|
||||||
this._stateHistory = computeHistory(
|
if (this._subscribed) {
|
||||||
this.hass,
|
this._unsubscribeHistory();
|
||||||
dateHistory,
|
}
|
||||||
this.hass.localize
|
|
||||||
);
|
const now = new Date();
|
||||||
} finally {
|
|
||||||
|
this._subscribed = subscribeHistory(
|
||||||
|
this.hass,
|
||||||
|
(history) => {
|
||||||
|
this._isLoading = false;
|
||||||
|
this._stateHistory = computeHistory(
|
||||||
|
this.hass,
|
||||||
|
history,
|
||||||
|
this.hass.localize
|
||||||
|
);
|
||||||
|
},
|
||||||
|
this._startDate,
|
||||||
|
this._endDate,
|
||||||
|
entityIds
|
||||||
|
);
|
||||||
|
this._subscribed.catch(() => {
|
||||||
this._isLoading = false;
|
this._isLoading = false;
|
||||||
|
this._unsubscribeHistory();
|
||||||
|
});
|
||||||
|
if (this._endDate > now) {
|
||||||
|
this._setRedrawTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setRedrawTimer() {
|
||||||
|
clearInterval(this._interval);
|
||||||
|
const now = new Date();
|
||||||
|
const end = this._endDate > now ? now : this._endDate;
|
||||||
|
const timespan = differenceInHours(this._startDate, end);
|
||||||
|
this._interval = window.setInterval(
|
||||||
|
() => this._stateHistoryCharts?.requestUpdate(),
|
||||||
|
// if timespan smaller than 1 hour, update every 10 seconds, smaller than 5 hours, redraw every minute, otherwise every 5 minutes
|
||||||
|
timespan < 2
|
||||||
|
? 10000
|
||||||
|
: timespan < 10
|
||||||
|
? 60 * 1000
|
||||||
|
: MIN_TIME_BETWEEN_UPDATES
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _unsubscribeHistory() {
|
||||||
|
if (this._interval) {
|
||||||
|
clearInterval(this._interval);
|
||||||
|
this._interval = undefined;
|
||||||
|
}
|
||||||
|
if (this._subscribed) {
|
||||||
|
this._subscribed.then((unsub) => unsub?.());
|
||||||
|
this._subscribed = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user