From d0cb7b972407fcd1d2b70b8586a595bf85edf2c0 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 23 Oct 2018 13:54:52 +0200 Subject: [PATCH] TS history data (#1839) * Convert history data to TS * Lint * Extract cached history * Move around --- src/cards/ha-history_graph-card.js | 2 +- src/components/state-history-chart-line.js | 2 +- src/data/cached-history.ts | 235 ++++++++++++ src/data/ha-state-history-data.js | 335 ++---------------- src/data/history.ts | 225 ++++++++++++ src/dialogs/more-info/more-info-controls.js | 2 +- src/panels/history/ha-panel-history.js | 2 +- .../lovelace/cards/hui-history-graph-card.js | 2 +- 8 files changed, 494 insertions(+), 311 deletions(-) create mode 100644 src/data/cached-history.ts create mode 100644 src/data/history.ts diff --git a/src/cards/ha-history_graph-card.js b/src/cards/ha-history_graph-card.js index 9cc6615d2e..ca2e3aad7b 100644 --- a/src/cards/ha-history_graph-card.js +++ b/src/cards/ha-history_graph-card.js @@ -3,7 +3,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag.js"; import { PolymerElement } from "@polymer/polymer/polymer-element.js"; import "../components/state-history-charts.js"; -import "../data/ha-state-history-data.js"; +import "../data/ha-state-history-data"; import computeStateName from "../common/entity/compute_state_name.js"; import EventsMixin from "../mixins/events-mixin.js"; diff --git a/src/components/state-history-chart-line.js b/src/components/state-history-chart-line.js index 91499c39c0..5981b68c9b 100644 --- a/src/components/state-history-chart-line.js +++ b/src/components/state-history-chart-line.js @@ -255,7 +255,7 @@ class StateHistoryChartLine extends LocalizeMixin(PolymerElement) { Array.prototype.push.apply(datasets, data); }); - const formatTooltipTitle = function(items, data) { + const formatTooltipTitle = (items, data) => { const item = items[0]; const date = data.datasets[item.datasetIndex].data[item.index].x; diff --git a/src/data/cached-history.ts b/src/data/cached-history.ts new file mode 100644 index 0000000000..a1b79fedb3 --- /dev/null +++ b/src/data/cached-history.ts @@ -0,0 +1,235 @@ +import { + computeHistory, + fetchRecent, + HistoryResult, + TimelineEntity, + LineChartUnit, +} from "./history"; +import { HomeAssistant } from "../types"; +import { LocalizeFunc } from "../mixins/localize-base-mixin"; +import { HassEntity } from "home-assistant-js-websocket"; + +interface CacheConfig { + refresh: number; + cacheKey: string; + hoursToShow: number; +} + +interface CachedResults { + prom: Promise; + startTime: Date; + endTime: Date; + language: string; + data: HistoryResult; +} + +// This is a different interface, a different cache :( +interface RecentCacheResults { + created: number; + language: string; + data: Promise; +} + +const RECENT_THRESHOLD = 60000; // 1 minute +const RECENT_CACHE: { [cacheKey: string]: RecentCacheResults } = {}; +const stateHistoryCache: { [cacheKey: string]: CachedResults } = {}; + +// Cached type 1 unction. Without cache config. +export const getRecent = ( + hass: HomeAssistant, + entityId: string, + startTime: Date, + endTime: Date, + localize: LocalizeFunc, + language: string +) => { + const cacheKey = entityId; + const cache = RECENT_CACHE[cacheKey]; + + if ( + cache && + Date.now() - cache.created < RECENT_THRESHOLD && + cache.language === language + ) { + return cache.data; + } + + const prom = fetchRecent(hass, entityId, startTime, endTime).then( + (stateHistory) => computeHistory(hass, stateHistory, localize, language), + (err) => { + delete RECENT_CACHE[entityId]; + throw err; + } + ); + + RECENT_CACHE[cacheKey] = { + created: Date.now(), + language, + data: prom, + }; + return prom; +}; + +// Cache type 2 functionality +function getEmptyCache( + language: string, + startTime: Date, + endTime: Date +): CachedResults { + return { + prom: Promise.resolve({ line: [], timeline: [] }), + language, + startTime, + endTime, + data: { line: [], timeline: [] }, + }; +} + +export const getRecentWithCache = ( + hass: HomeAssistant, + entityId: string, + cacheConfig: CacheConfig, + localize: LocalizeFunc, + language: string +) => { + const cacheKey = cacheConfig.cacheKey; + const endTime = new Date(); + const startTime = new Date(endTime); + startTime.setHours(startTime.getHours() - cacheConfig.hoursToShow); + let toFetchStartTime = startTime; + let appendingToCache = false; + + let cache = stateHistoryCache[cacheKey]; + if ( + cache && + toFetchStartTime >= cache.startTime && + toFetchStartTime <= cache.endTime && + cache.language === language + ) { + toFetchStartTime = cache.endTime; + appendingToCache = true; + // This pretty much never happens as endTime is usually set to now + if (endTime <= cache.endTime) { + return cache.prom; + } + } else { + cache = stateHistoryCache[cacheKey] = getEmptyCache( + language, + startTime, + endTime + ); + } + + const curCacheProm = cache.prom; + + const genProm = async () => { + let fetchedHistory: HassEntity[][]; + + try { + const results = await Promise.all([ + curCacheProm, + fetchRecent( + hass, + entityId, + toFetchStartTime, + endTime, + appendingToCache + ), + ]); + fetchedHistory = results[1]; + } catch (err) { + delete stateHistoryCache[cacheKey]; + throw err; + } + const stateHistory = computeHistory( + hass, + fetchedHistory, + localize, + language + ); + if (appendingToCache) { + mergeLine(stateHistory.line, cache.data.line); + mergeTimeline(stateHistory.timeline, cache.data.timeline); + pruneStartTime(startTime, cache.data); + } else { + cache.data = stateHistory; + } + return cache.data; + }; + + cache.prom = genProm(); + cache.startTime = startTime; + cache.endTime = endTime; + return cache.prom; +}; + +const mergeLine = ( + historyLines: LineChartUnit[], + cacheLines: LineChartUnit[] +) => { + historyLines.forEach((line) => { + const unit = line.unit; + const oldLine = cacheLines.find((cacheLine) => cacheLine.unit === unit); + if (oldLine) { + line.data.forEach((entity) => { + const oldEntity = oldLine.data.find( + (cacheEntity) => entity.entity_id === cacheEntity.entity_id + ); + if (oldEntity) { + oldEntity.states = oldEntity.states.concat(entity.states); + } else { + oldLine.data.push(entity); + } + }); + } else { + cacheLines.push(line); + } + }); +}; + +const mergeTimeline = ( + historyTimelines: TimelineEntity[], + cacheTimelines: TimelineEntity[] +) => { + historyTimelines.forEach((timeline) => { + const oldTimeline = cacheTimelines.find( + (cacheTimeline) => cacheTimeline.entity_id === timeline.entity_id + ); + if (oldTimeline) { + oldTimeline.data = oldTimeline.data.concat(timeline.data); + } else { + cacheTimelines.push(timeline); + } + }); +}; + +const pruneArray = (originalStartTime: Date, arr) => { + if (arr.length === 0) { + return arr; + } + const changedAfterStartTime = arr.findIndex( + (state) => new Date(state.last_changed) > originalStartTime + ); + if (changedAfterStartTime === 0) { + // If all changes happened after originalStartTime then we are done. + return arr; + } + + // If all changes happened at or before originalStartTime. Use last index. + const updateIndex = + changedAfterStartTime === -1 ? arr.length - 1 : changedAfterStartTime - 1; + arr[updateIndex].last_changed = originalStartTime; + return arr.slice(updateIndex); +}; + +const pruneStartTime = (originalStartTime: Date, cacheData: HistoryResult) => { + cacheData.line.forEach((line) => { + line.data.forEach((entity) => { + entity.states = pruneArray(originalStartTime, entity.states); + }); + }); + + cacheData.timeline.forEach((timeline) => { + timeline.data = pruneArray(originalStartTime, timeline.data); + }); +}; diff --git a/src/data/ha-state-history-data.js b/src/data/ha-state-history-data.js index 94a0f1126a..f366b1039d 100644 --- a/src/data/ha-state-history-data.js +++ b/src/data/ha-state-history-data.js @@ -2,120 +2,10 @@ import { timeOut } from "@polymer/polymer/lib/utils/async.js"; import { Debouncer } from "@polymer/polymer/lib/utils/debounce.js"; import { PolymerElement } from "@polymer/polymer/polymer-element.js"; -import computeStateName from "../common/entity/compute_state_name.js"; -import computeStateDomain from "../common/entity/compute_state_domain.js"; -import computeStateDisplay from "../common/entity/compute_state_display.js"; import LocalizeMixin from "../mixins/localize-mixin.js"; -const RECENT_THRESHOLD = 60000; // 1 minute -const RECENT_CACHE = {}; -const DOMAINS_USE_LAST_UPDATED = ["thermostat", "climate", "water_heater"]; -const LINE_ATTRIBUTES_TO_KEEP = [ - "temperature", - "current_temperature", - "target_temp_low", - "target_temp_high", -]; -const stateHistoryCache = {}; - -function computeHistory(hass, stateHistory, localize, language) { - const lineChartDevices = {}; - const timelineDevices = []; - if (!stateHistory) { - return { line: [], timeline: [] }; - } - - stateHistory.forEach((stateInfo) => { - if (stateInfo.length === 0) { - return; - } - - const stateWithUnit = stateInfo.find( - (state) => "unit_of_measurement" in state.attributes - ); - - let unit = false; - if (stateWithUnit) { - unit = stateWithUnit.attributes.unit_of_measurement; - } else if (computeStateDomain(stateInfo[0]) === "climate") { - unit = hass.config.unit_system.temperature; - } else if (computeStateDomain(stateInfo[0]) === "water_heater") { - unit = hass.config.unit_system.temperature; - } - - if (!unit) { - timelineDevices.push({ - name: computeStateName(stateInfo[0]), - entity_id: stateInfo[0].entity_id, - data: stateInfo - .map((state) => ({ - state_localize: computeStateDisplay(localize, state, language), - state: state.state, - last_changed: state.last_changed, - })) - .filter((element, index, arr) => { - if (index === 0) return true; - return element.state !== arr[index - 1].state; - }), - }); - } else if (unit in lineChartDevices) { - lineChartDevices[unit].push(stateInfo); - } else { - lineChartDevices[unit] = [stateInfo]; - } - }); - - const unitStates = Object.keys(lineChartDevices).map((unit) => ({ - unit: unit, - identifier: lineChartDevices[unit] - .map((states) => states[0].entity_id) - .join(""), - data: lineChartDevices[unit].map((states) => { - const last = states[states.length - 1]; - const domain = computeStateDomain(last); - return { - domain: domain, - name: computeStateName(last), - entity_id: last.entity_id, - states: states - .map((state) => { - const result = { - state: state.state, - last_changed: state.last_changed, - }; - if (DOMAINS_USE_LAST_UPDATED.includes(domain)) { - result.last_changed = state.last_updated; - } - LINE_ATTRIBUTES_TO_KEEP.forEach((attr) => { - if (attr in state.attributes) { - result.attributes = result.attributes || {}; - result.attributes[attr] = state.attributes[attr]; - } - }); - return result; - }) - .filter((element, index, arr) => { - // Remove data point if it is equal to previous point and next point. - if (index === 0 || index === arr.length - 1) return true; - function compare(obj1, obj2) { - if (obj1.state !== obj2.state) return false; - if (!obj1.attributes && !obj2.attributes) return true; - if (!obj1.attributes || !obj2.attributes) return false; - return LINE_ATTRIBUTES_TO_KEEP.every( - (attr) => obj1.attributes[attr] === obj2.attributes[attr] - ); - } - return ( - !compare(element, arr[index - 1]) || - !compare(element, arr[index + 1]) - ); - }), - }; - }), - })); - - return { line: unitStates, timeline: timelineDevices }; -} +import { computeHistory, fetchDate } from "./history"; +import { getRecent, getRecentWithCache } from "./cached-history"; /* * @appliesMixin LocalizeMixin @@ -225,7 +115,10 @@ class HaStateHistoryData extends LocalizeMixin(PolymerElement) { if (filterType === "date") { if (!startTime || !endTime) return; - data = this.getDate(startTime, endTime, localize, language); + + data = fetchDate(this.hass, startTime, endTime).then((dateHistory) => + computeHistory(this.hass, dateHistory, localize, language) + ); } else if (filterType === "recent-entity") { if (!entityId) return; if (cacheConfig) { @@ -236,7 +129,14 @@ class HaStateHistoryData extends LocalizeMixin(PolymerElement) { language ); } else { - data = this.getRecent(entityId, startTime, endTime, localize, language); + data = getRecent( + this.hass, + entityId, + startTime, + endTime, + localize, + language + ); } } else { return; @@ -249,14 +149,6 @@ class HaStateHistoryData extends LocalizeMixin(PolymerElement) { }); } - getEmptyCache(language) { - return { - prom: Promise.resolve({ line: [], timeline: [] }), - language: language, - data: { line: [], timeline: [] }, - }; - } - getRecentWithCacheRefresh(entityId, cacheConfig, localize, language) { if (this._refreshTimeoutId) { window.clearInterval(this._refreshTimeoutId); @@ -264,193 +156,24 @@ class HaStateHistoryData extends LocalizeMixin(PolymerElement) { } if (cacheConfig.refresh) { this._refreshTimeoutId = window.setInterval(() => { - this.getRecentWithCache(entityId, cacheConfig, localize, language).then( - (stateHistory) => { - this._setData(Object.assign({}, stateHistory)); - } - ); + getRecentWithCache( + this.hass, + entityId, + cacheConfig, + localize, + language + ).then((stateHistory) => { + this._setData(Object.assign({}, stateHistory)); + }); }, cacheConfig.refresh * 1000); } - return this.getRecentWithCache(entityId, cacheConfig, localize, language); - } - - mergeLine(historyLines, cacheLines) { - historyLines.forEach((line) => { - const unit = line.unit; - const oldLine = cacheLines.find((cacheLine) => cacheLine.unit === unit); - if (oldLine) { - line.data.forEach((entity) => { - const oldEntity = oldLine.data.find( - (cacheEntity) => entity.entity_id === cacheEntity.entity_id - ); - if (oldEntity) { - oldEntity.states = oldEntity.states.concat(entity.states); - } else { - oldLine.data.push(entity); - } - }); - } else { - cacheLines.push(line); - } - }); - } - - mergeTimeline(historyTimelines, cacheTimelines) { - historyTimelines.forEach((timeline) => { - const oldTimeline = cacheTimelines.find( - (cacheTimeline) => cacheTimeline.entity_id === timeline.entity_id - ); - if (oldTimeline) { - oldTimeline.data = oldTimeline.data.concat(timeline.data); - } else { - cacheTimelines.push(timeline); - } - }); - } - - pruneArray(originalStartTime, arr) { - if (arr.length === 0) return arr; - const changedAfterStartTime = arr.findIndex((state) => { - const lastChanged = new Date(state.last_changed); - return lastChanged > originalStartTime; - }); - if (changedAfterStartTime === 0) { - // If all changes happened after originalStartTime then we are done. - return arr; - } - - // If all changes happened at or before originalStartTime. Use last index. - const updateIndex = - changedAfterStartTime === -1 ? arr.length - 1 : changedAfterStartTime - 1; - arr[updateIndex].last_changed = originalStartTime; - return arr.slice(updateIndex); - } - - pruneStartTime(originalStartTime, cacheData) { - cacheData.line.forEach((line) => { - line.data.forEach((entity) => { - entity.states = this.pruneArray(originalStartTime, entity.states); - }); - }); - - cacheData.timeline.forEach((timeline) => { - timeline.data = this.pruneArray(originalStartTime, timeline.data); - }); - } - - getRecentWithCache(entityId, cacheConfig, localize, language) { - const cacheKey = cacheConfig.cacheKey; - const endTime = new Date(); - const originalStartTime = new Date(endTime); - originalStartTime.setHours( - originalStartTime.getHours() - cacheConfig.hoursToShow + return getRecentWithCache( + this.hass, + entityId, + cacheConfig, + localize, + language ); - let startTime = originalStartTime; - let appendingToCache = false; - let cache = stateHistoryCache[cacheKey]; - if ( - cache && - startTime >= cache.startTime && - startTime <= cache.endTime && - cache.language === language - ) { - startTime = cache.endTime; - appendingToCache = true; - if (endTime <= cache.endTime) { - return cache.prom; - } - } else { - cache = stateHistoryCache[cacheKey] = this.getEmptyCache(language); - } - // Use Promise.all in order to make sure the old and the new fetches have both completed. - const prom = Promise.all([ - cache.prom, - this.fetchRecent(entityId, startTime, endTime, appendingToCache), - ]) - // Use only data from the new fetch. Old fetch is already stored in cache.data - .then((oldAndNew) => oldAndNew[1]) - // Convert data into format state-history-chart-* understands. - .then((stateHistory) => - computeHistory(this.hass, stateHistory, localize, language) - ) - // Merge old and new. - .then((stateHistory) => { - this.mergeLine(stateHistory.line, cache.data.line); - this.mergeTimeline(stateHistory.timeline, cache.data.timeline); - if (appendingToCache) { - this.pruneStartTime(originalStartTime, cache.data); - } - return cache.data; - }) - .catch((err) => { - /* eslint-disable no-console */ - console.error(err); - stateHistoryCache[cacheKey] = undefined; - }); - cache.prom = prom; - cache.startTime = originalStartTime; - cache.endTime = endTime; - return prom; - } - - getRecent(entityId, startTime, endTime, localize, language) { - const cacheKey = entityId; - const cache = RECENT_CACHE[cacheKey]; - - if ( - cache && - Date.now() - cache.created < RECENT_THRESHOLD && - cache.language === language - ) { - return cache.data; - } - - const prom = this.fetchRecent(entityId, startTime, endTime).then( - (stateHistory) => - computeHistory(this.hass, stateHistory, localize, language), - () => { - RECENT_CACHE[entityId] = false; - return null; - } - ); - - RECENT_CACHE[cacheKey] = { - created: Date.now(), - language: language, - data: prom, - }; - return prom; - } - - fetchRecent(entityId, startTime, endTime, skipInitialState = false) { - let url = "history/period"; - if (startTime) { - url += "/" + startTime.toISOString(); - } - url += "?filter_entity_id=" + entityId; - if (endTime) { - url += "&end_time=" + endTime.toISOString(); - } - if (skipInitialState) { - url += "&skip_initial_state"; - } - - return this.hass.callApi("GET", url); - } - - getDate(startTime, endTime, localize, language) { - const filter = - startTime.toISOString() + "?end_time=" + endTime.toISOString(); - - const prom = this.hass - .callApi("GET", "history/period/" + filter) - .then( - (stateHistory) => - computeHistory(this.hass, stateHistory, localize, language), - () => null - ); - - return prom; } } customElements.define("ha-state-history-data", HaStateHistoryData); diff --git a/src/data/history.ts b/src/data/history.ts new file mode 100644 index 0000000000..de28f78cff --- /dev/null +++ b/src/data/history.ts @@ -0,0 +1,225 @@ +import computeStateName from "../common/entity/compute_state_name.js"; +import computeStateDomain from "../common/entity/compute_state_domain.js"; +import computeStateDisplay from "../common/entity/compute_state_display.js"; +import { HassEntity } from "home-assistant-js-websocket"; +import { LocalizeFunc } from "../mixins/localize-base-mixin.js"; +import { HomeAssistant } from "../types.js"; + +const DOMAINS_USE_LAST_UPDATED = ["climate", "water_heater"]; +const LINE_ATTRIBUTES_TO_KEEP = [ + "temperature", + "current_temperature", + "target_temp_low", + "target_temp_high", +]; + +export interface LineChartState { + state: string; + last_changed: string; + attributes?: { [key: string]: any }; +} + +export interface LineChartEntity { + domain: string; + name: string; + entity_id: string; + states: LineChartState[]; +} + +export interface LineChartUnit { + unit: string; + identifier: string; + data: LineChartEntity[]; +} + +export interface TimelineState { + state_localize: string; + state: string; + last_changed: string; +} + +export interface TimelineEntity { + name: string; + entity_id: string; + data: TimelineState[]; +} + +export interface HistoryResult { + line: LineChartUnit[]; + timeline: TimelineEntity[]; +} + +export const fetchRecent = ( + hass, + entityId, + startTime, + endTime, + skipInitialState = false +): Promise => { + let url = "history/period"; + if (startTime) { + url += "/" + startTime.toISOString(); + } + url += "?filter_entity_id=" + entityId; + if (endTime) { + url += "&end_time=" + endTime.toISOString(); + } + if (skipInitialState) { + url += "&skip_initial_state"; + } + + return hass.callApi("GET", url); +}; + +export const fetchDate = ( + hass: HomeAssistant, + startTime: Date, + endTime: Date +): Promise => { + return hass.callApi( + "GET", + `history/period/${startTime.toISOString()}?end_time=${endTime.toISOString()}` + ); +}; + +const equalState = (obj1: LineChartState, obj2: LineChartState) => + obj1.state === obj2.state && + // They either both have an attributes object or not + (!obj1.attributes || + LINE_ATTRIBUTES_TO_KEEP.every( + (attr) => obj1.attributes![attr] === obj2.attributes![attr] + )); + +const processTimelineEntity = ( + localize: LocalizeFunc, + language: string, + states: HassEntity[] +): TimelineEntity => { + const data: TimelineState[] = []; + + for (const state of states) { + if (data.length > 0 && state.state === data[data.length - 1].state) { + continue; + } + + data.push({ + state_localize: computeStateDisplay(localize, state, language), + state: state.state, + last_changed: state.last_changed, + }); + } + + return { + name: computeStateName(states[0]), + entity_id: states[0].entity_id, + data, + }; +}; + +const processLineChartEntities = ( + unit, + entities: HassEntity[][] +): LineChartUnit => { + const data: LineChartEntity[] = []; + + for (const states of entities) { + const last: HassEntity = states[states.length - 1]; + const domain = computeStateDomain(last); + const processedStates: LineChartState[] = []; + + for (const state of states) { + let processedState: LineChartState; + + if (DOMAINS_USE_LAST_UPDATED.includes(domain)) { + processedState = { + state: state.state, + last_changed: state.last_updated, + attributes: {}, + }; + + for (const attr of LINE_ATTRIBUTES_TO_KEEP) { + if (attr in state.attributes) { + processedState.attributes![attr] = state.attributes[attr]; + } + } + } else { + processedState = state; + } + + if ( + processedStates.length > 1 && + equalState( + processedState, + processedStates[processedStates.length - 1] + ) && + equalState(processedState, processedStates[processedStates.length - 2]) + ) { + continue; + } + + processedStates.push(processedState); + } + + data.push({ + domain, + name: computeStateName(last), + entity_id: last.entity_id, + states: processedStates, + }); + } + + return { + unit, + identifier: entities.map((states) => states[0].entity_id).join(""), + data, + }; +}; + +export const computeHistory = ( + hass: HomeAssistant, + stateHistory: HassEntity[][], + localize: LocalizeFunc, + language: string +): HistoryResult => { + const lineChartDevices: { [unit: string]: HassEntity[][] } = {}; + const timelineDevices: TimelineEntity[] = []; + if (!stateHistory) { + return { line: [], timeline: [] }; + } + + stateHistory.forEach((stateInfo) => { + if (stateInfo.length === 0) { + return; + } + + const stateWithUnit = stateInfo.find( + (state) => "unit_of_measurement" in state.attributes + ); + + let unit: string | undefined; + + if (stateWithUnit) { + unit = stateWithUnit.attributes.unit_of_measurement; + } else if (computeStateDomain(stateInfo[0]) === "climate") { + unit = hass.config.unit_system.temperature; + } else if (computeStateDomain(stateInfo[0]) === "water_heater") { + unit = hass.config.unit_system.temperature; + } + + if (!unit) { + timelineDevices.push( + processTimelineEntity(localize, language, stateInfo) + ); + } else if (unit in lineChartDevices) { + lineChartDevices[unit].push(stateInfo); + } else { + lineChartDevices[unit] = [stateInfo]; + } + }); + + const unitStates = Object.keys(lineChartDevices).map((unit) => + processLineChartEntities(unit, lineChartDevices[unit]) + ); + + return { line: unitStates, timeline: timelineDevices }; +}; diff --git a/src/dialogs/more-info/more-info-controls.js b/src/dialogs/more-info/more-info-controls.js index 2961d356e4..a23cf38be8 100644 --- a/src/dialogs/more-info/more-info-controls.js +++ b/src/dialogs/more-info/more-info-controls.js @@ -5,7 +5,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag.js"; import { PolymerElement } from "@polymer/polymer/polymer-element.js"; import "../../components/state-history-charts.js"; -import "../../data/ha-state-history-data.js"; +import "../../data/ha-state-history-data"; import "../../resources/ha-style.js"; import "../../state-summary/state-card-content.js"; diff --git a/src/panels/history/ha-panel-history.js b/src/panels/history/ha-panel-history.js index 376015bedc..3b3732923c 100644 --- a/src/panels/history/ha-panel-history.js +++ b/src/panels/history/ha-panel-history.js @@ -12,7 +12,7 @@ import "@vaadin/vaadin-date-picker/vaadin-date-picker.js"; import "../../components/ha-menu-button.js"; import "../../components/state-history-charts.js"; -import "../../data/ha-state-history-data.js"; +import "../../data/ha-state-history-data"; import "../../resources/ha-date-picker-style.js"; import "../../resources/ha-style.js"; diff --git a/src/panels/lovelace/cards/hui-history-graph-card.js b/src/panels/lovelace/cards/hui-history-graph-card.js index 4c982abbe7..7a3f4dfdb8 100644 --- a/src/panels/lovelace/cards/hui-history-graph-card.js +++ b/src/panels/lovelace/cards/hui-history-graph-card.js @@ -3,7 +3,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element.js"; import "../../../components/ha-card.js"; import "../../../components/state-history-charts.js"; -import "../../../data/ha-state-history-data.js"; +import "../../../data/ha-state-history-data"; import processConfigEntities from "../common/process-config-entities.js";