Merge branch 'remove_legacy_history_calls' into int2
This commit is contained in:
commit
d154872955
|
@ -94,73 +94,6 @@ export const entityIdHistoryNeedsAttributes = (
|
|||
!hass.states[entityId] ||
|
||||
NEED_ATTRIBUTE_DOMAINS.includes(computeDomain(entityId));
|
||||
|
||||
export const fetchRecent = (
|
||||
hass: HomeAssistant,
|
||||
entityId: string,
|
||||
startTime: Date,
|
||||
endTime: Date,
|
||||
skipInitialState = false,
|
||||
significantChangesOnly?: boolean,
|
||||
minimalResponse = true,
|
||||
noAttributes?: boolean
|
||||
): Promise<HassEntity[][]> => {
|
||||
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";
|
||||
}
|
||||
if (significantChangesOnly !== undefined) {
|
||||
url += `&significant_changes_only=${Number(significantChangesOnly)}`;
|
||||
}
|
||||
if (minimalResponse) {
|
||||
url += "&minimal_response";
|
||||
}
|
||||
if (noAttributes) {
|
||||
url += "&no_attributes";
|
||||
}
|
||||
return hass.callApi("GET", url);
|
||||
};
|
||||
|
||||
export const fetchRecentWS = (
|
||||
hass: HomeAssistant,
|
||||
entityIds: string[],
|
||||
startTime: Date,
|
||||
endTime: Date,
|
||||
skipInitialState = false,
|
||||
significantChangesOnly?: boolean,
|
||||
minimalResponse = true,
|
||||
noAttributes?: boolean
|
||||
) =>
|
||||
hass.callWS<HistoryStates>({
|
||||
type: "history/history_during_period",
|
||||
start_time: startTime.toISOString(),
|
||||
end_time: endTime.toISOString(),
|
||||
significant_changes_only: significantChangesOnly || false,
|
||||
include_start_time_state: !skipInitialState,
|
||||
minimal_response: minimalResponse,
|
||||
no_attributes: noAttributes || false,
|
||||
entity_ids: entityIds,
|
||||
});
|
||||
|
||||
export const fetchDate = (
|
||||
hass: HomeAssistant,
|
||||
startTime: Date,
|
||||
endTime: Date,
|
||||
entityIds: string[]
|
||||
): Promise<HassEntity[][]> =>
|
||||
hass.callApi(
|
||||
"GET",
|
||||
`history/period/${startTime.toISOString()}?end_time=${endTime.toISOString()}&minimal_response${
|
||||
entityIds ? `&filter_entity_id=${entityIds.join(",")}` : ``
|
||||
}`
|
||||
);
|
||||
|
||||
export const fetchDateWS = (
|
||||
hass: HomeAssistant,
|
||||
startTime: Date,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HassEntities, HassEntity } from "home-assistant-js-websocket";
|
||||
import { HassEntities } from "home-assistant-js-websocket";
|
||||
import { LatLngTuple } from "leaflet";
|
||||
import {
|
||||
css,
|
||||
|
@ -12,11 +12,15 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||
import { mdiImageFilterCenterFocus } from "@mdi/js";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isToday } from "date-fns";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import { fetchRecent } from "../../../data/history";
|
||||
import {
|
||||
HistoryStates,
|
||||
subscribeHistoryStatesTimeWindow,
|
||||
} from "../../../data/history";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { processConfigEntities } from "../common/process-config-entities";
|
||||
|
@ -36,8 +40,7 @@ import {
|
|||
formatTimeWeekday,
|
||||
} from "../../../common/datetime/format_time";
|
||||
|
||||
const MINUTE = 60000;
|
||||
|
||||
const DEFAULT_HOURS_TO_SHOW = 24;
|
||||
@customElement("hui-map-card")
|
||||
class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
@ -45,8 +48,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
@property({ type: Boolean, reflect: true })
|
||||
public isPanel = false;
|
||||
|
||||
@state()
|
||||
private _history?: HassEntity[][];
|
||||
@state() private _stateHistory?: HistoryStates;
|
||||
|
||||
@state()
|
||||
private _config?: MapCardConfig;
|
||||
|
@ -54,14 +56,16 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
@query("ha-map")
|
||||
private _map?: HaMap;
|
||||
|
||||
private _date?: Date;
|
||||
|
||||
private _configEntities?: string[];
|
||||
|
||||
private _colorDict: Record<string, string> = {};
|
||||
|
||||
private _colorIndex = 0;
|
||||
|
||||
private _error?: string;
|
||||
|
||||
private _subscribed?: Promise<(() => Promise<void>) | void>;
|
||||
|
||||
public setConfig(config: MapCardConfig): void {
|
||||
if (!config) {
|
||||
throw new Error("Error in card configuration.");
|
||||
|
@ -88,8 +92,6 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
? processConfigEntities<EntityConfig>(config.entities)
|
||||
: []
|
||||
).map((entity) => entity.entity);
|
||||
|
||||
this._cleanupHistory();
|
||||
}
|
||||
|
||||
public getCardSize(): number {
|
||||
|
@ -133,6 +135,9 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
if (!this._config) {
|
||||
return html``;
|
||||
}
|
||||
if (this._error) {
|
||||
return html`<div class="error">${this._error}</div>`;
|
||||
}
|
||||
return html`
|
||||
<ha-card id="card" .header=${this._config.title}>
|
||||
<div id="root">
|
||||
|
@ -144,7 +149,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
this._configEntities
|
||||
)}
|
||||
.zoom=${this._config.default_zoom ?? 14}
|
||||
.paths=${this._getHistoryPaths(this._config, this._history)}
|
||||
.paths=${this._getHistoryPaths(this._config, this._stateHistory)}
|
||||
.autoFit=${this._config.auto_fit}
|
||||
.darkMode=${this._config.dark_mode}
|
||||
></ha-map>
|
||||
|
@ -176,23 +181,68 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Check if any state has changed
|
||||
for (const entity of this._configEntities) {
|
||||
if (oldHass.states[entity] !== this.hass!.states[entity]) {
|
||||
return true;
|
||||
}
|
||||
if (changedProps.has("_stateHistory")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (this._config?.hours_to_show && this._configEntities?.length) {
|
||||
if (changedProps.has("_config")) {
|
||||
this._getHistory();
|
||||
} else if (Date.now() - this._date!.getTime() >= MINUTE) {
|
||||
this._getHistory();
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.hasUpdated && this._configEntities?.length) {
|
||||
this._subscribeHistoryTimeWindow();
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unsubscribeHistoryTimeWindow();
|
||||
}
|
||||
|
||||
private _subscribeHistoryTimeWindow() {
|
||||
if (!isComponentLoaded(this.hass!, "history") || this._subscribed) {
|
||||
return;
|
||||
}
|
||||
this._subscribed = subscribeHistoryStatesTimeWindow(
|
||||
this.hass!,
|
||||
(combinedHistory) => {
|
||||
if (!this._subscribed) {
|
||||
// Message came in before we had a chance to unload
|
||||
return;
|
||||
}
|
||||
this._stateHistory = combinedHistory;
|
||||
},
|
||||
this._config!.hours_to_show! || DEFAULT_HOURS_TO_SHOW,
|
||||
this._configEntities!,
|
||||
false,
|
||||
false
|
||||
).catch((err) => {
|
||||
this._subscribed = undefined;
|
||||
this._error = err;
|
||||
});
|
||||
}
|
||||
|
||||
private _unsubscribeHistoryTimeWindow() {
|
||||
if (!this._subscribed) {
|
||||
return;
|
||||
}
|
||||
this._subscribed.then((unsubscribe) => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
this._subscribed = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (this._configEntities?.length) {
|
||||
if (!this._subscribed || changedProps.has("_config")) {
|
||||
this._unsubscribeHistoryTimeWindow();
|
||||
this._subscribeHistoryTimeWindow();
|
||||
}
|
||||
} else {
|
||||
this._unsubscribeHistoryTimeWindow();
|
||||
}
|
||||
if (changedProps.has("_config")) {
|
||||
this._computePadding();
|
||||
|
@ -272,46 +322,44 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
private _getHistoryPaths = memoizeOne(
|
||||
(
|
||||
config: MapCardConfig,
|
||||
history?: HassEntity[][]
|
||||
history?: HistoryStates
|
||||
): HaMapPaths[] | undefined => {
|
||||
if (!config.hours_to_show || !history) {
|
||||
if (!history) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const paths: HaMapPaths[] = [];
|
||||
|
||||
for (const entityStates of history) {
|
||||
if (entityStates?.length <= 1) {
|
||||
for (const entityId of Object.keys(history)) {
|
||||
const entityStates = history[entityId];
|
||||
if (!entityStates?.length) {
|
||||
continue;
|
||||
}
|
||||
// filter location data from states and remove all invalid locations
|
||||
const points = entityStates.reduce(
|
||||
(accumulator: HaMapPathPoint[], entityState) => {
|
||||
const latitude = entityState.attributes.latitude;
|
||||
const longitude = entityState.attributes.longitude;
|
||||
if (latitude && longitude) {
|
||||
const p = {} as HaMapPathPoint;
|
||||
p.point = [latitude, longitude] as LatLngTuple;
|
||||
const t = new Date(entityState.last_updated);
|
||||
if (config.hours_to_show! > 144) {
|
||||
// if showing > 6 days in the history trail, show the full
|
||||
// date and time
|
||||
p.tooltip = formatDateTime(t, this.hass.locale);
|
||||
} else if (isToday(t)) {
|
||||
p.tooltip = formatTime(t, this.hass.locale);
|
||||
} else {
|
||||
p.tooltip = formatTimeWeekday(t, this.hass.locale);
|
||||
}
|
||||
accumulator.push(p);
|
||||
}
|
||||
return accumulator;
|
||||
},
|
||||
[]
|
||||
) as HaMapPathPoint[];
|
||||
|
||||
const points: HaMapPathPoint[] = [];
|
||||
for (const entityState of entityStates) {
|
||||
const latitude = entityState.a.latitude;
|
||||
const longitude = entityState.a.longitude;
|
||||
if (!latitude || !longitude) {
|
||||
continue;
|
||||
}
|
||||
const p = {} as HaMapPathPoint;
|
||||
p.point = [latitude, longitude] as LatLngTuple;
|
||||
const t = new Date(entityState.lu * 1000);
|
||||
if (config.hours_to_show! || DEFAULT_HOURS_TO_SHOW > 144) {
|
||||
// if showing > 6 days in the history trail, show the full
|
||||
// date and time
|
||||
p.tooltip = formatDateTime(t, this.hass.locale);
|
||||
} else if (isToday(t)) {
|
||||
p.tooltip = formatTime(t, this.hass.locale);
|
||||
} else {
|
||||
p.tooltip = formatTimeWeekday(t, this.hass.locale);
|
||||
}
|
||||
points.push(p);
|
||||
}
|
||||
paths.push({
|
||||
points,
|
||||
color: this._getColor(entityStates[0].entity_id),
|
||||
color: this._getColor(entityId),
|
||||
gradualOpacity: 0.8,
|
||||
});
|
||||
}
|
||||
|
@ -319,58 +367,6 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
|||
}
|
||||
);
|
||||
|
||||
private async _getHistory(): Promise<void> {
|
||||
this._date = new Date();
|
||||
|
||||
if (!this._configEntities) {
|
||||
return;
|
||||
}
|
||||
|
||||
const entityIds = this._configEntities!.join(",");
|
||||
const endTime = new Date();
|
||||
const startTime = new Date();
|
||||
startTime.setHours(endTime.getHours() - this._config!.hours_to_show!);
|
||||
const skipInitialState = false;
|
||||
const significantChangesOnly = false;
|
||||
const minimalResponse = false;
|
||||
|
||||
const stateHistory = await fetchRecent(
|
||||
this.hass,
|
||||
entityIds,
|
||||
startTime,
|
||||
endTime,
|
||||
skipInitialState,
|
||||
significantChangesOnly,
|
||||
minimalResponse
|
||||
);
|
||||
|
||||
if (stateHistory.length < 1) {
|
||||
return;
|
||||
}
|
||||
this._history = stateHistory;
|
||||
}
|
||||
|
||||
private _cleanupHistory() {
|
||||
if (!this._history) {
|
||||
return;
|
||||
}
|
||||
if (this._config!.hours_to_show! <= 0) {
|
||||
this._history = undefined;
|
||||
} else {
|
||||
// remove unused entities
|
||||
this._history = this._history!.reduce(
|
||||
(accumulator: HassEntity[][], entityStates) => {
|
||||
const entityId = entityStates[0].entity_id;
|
||||
if (this._configEntities?.includes(entityId)) {
|
||||
accumulator.push(entityStates);
|
||||
}
|
||||
return accumulator;
|
||||
},
|
||||
[]
|
||||
) as HassEntity[][];
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-card {
|
||||
|
|
Loading…
Reference in New Issue