Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/data/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ export const subscribeHistory = (
const stream = new HistoryStream(hass);
return hass.connection.subscribeMessage<HistoryStreamMessage>(
(message) => callbackFunction(stream.processMessage(message)),
params
params,
{ resubscribe: false }
);
};

Expand Down Expand Up @@ -256,7 +257,8 @@ export const subscribeHistoryStatesTimeWindow = (
const stream = new HistoryStream(hass, hoursToShow);
return hass.connection.subscribeMessage<HistoryStreamMessage>(
(message) => callbackFunction(stream.processMessage(message)),
params
params,
{ resubscribe: false }
);
};

Expand Down
11 changes: 8 additions & 3 deletions src/data/sensor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ export const getSensorNumericDeviceClasses = async (
if (sensorNumericDeviceClassesCache) {
return sensorNumericDeviceClassesCache;
}
sensorNumericDeviceClassesCache = hass.callWS({
type: "sensor/numeric_device_classes",
});
sensorNumericDeviceClassesCache = hass
.callWS<SensorNumericDeviceClasses>({
type: "sensor/numeric_device_classes",
})
.catch((err: Error) => {
sensorNumericDeviceClassesCache = undefined;
throw err;
});
return sensorNumericDeviceClassesCache!;
};
58 changes: 55 additions & 3 deletions src/dialogs/more-info/ha-more-info-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,22 @@ export class MoreInfoHistory extends LitElement {
this._showMoreHref = `/history?${createSearchParam(params)}`;

this._getStateHistory();
} else if (
changedProps.has("hass") &&
this.entityId &&
!this._subscribed &&
!this._stateHistory &&
!this._statistics &&
!this._error
) {
// Retry when components become available after backend restart
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
oldHass &&
oldHass.config.components !== this.hass.config.components
) {
this._getStateHistory();
}
}
}

Expand All @@ -131,17 +147,34 @@ export class MoreInfoHistory extends LitElement {
if (this.hasUpdated && this.entityId) {
this._getStateHistory();
}
window.addEventListener("connection-status", this._handleConnectionStatus);
}

public disconnectedCallback() {
super.disconnectedCallback();
this._unsubscribeHistory();
window.removeEventListener(
"connection-status",
this._handleConnectionStatus
);
}

private _handleConnectionStatus = (ev: Event) => {
if ((ev as CustomEvent).detail === "connected") {
this._unsubscribeHistory();
this._stateHistory = undefined;
this._statistics = undefined;
this._error = undefined;
if (this.entityId) {
this._getStateHistory();
}
}
};

private _unsubscribeHistory() {
clearInterval(this._interval);
if (this._subscribed) {
this._subscribed.then((unsub) => unsub?.());
this._subscribed.then((unsub) => unsub?.()).catch(() => undefined);
this._subscribed = undefined;
}
}
Expand Down Expand Up @@ -228,8 +261,27 @@ export class MoreInfoHistory extends LitElement {
this._unsubscribeHistory();
}

const { numeric_device_classes: sensorNumericDeviceClasses } =
await getSensorNumericDeviceClasses(this.hass);
// Mark as subscribing before the await to prevent re-entrant calls
const sentinel = Promise.resolve(undefined) as NonNullable<
typeof this._subscribed
>;
this._subscribed = sentinel;

let sensorNumericDeviceClasses: string[];
try {
({ numeric_device_classes: sensorNumericDeviceClasses } =
await getSensorNumericDeviceClasses(this.hass));
} catch (_err) {
if (this._subscribed === sentinel) {
this._subscribed = undefined;
}
return;
}

// Bail out if a newer call replaced our sentinel while we were awaiting
if (this._subscribed !== sentinel) {
return;
}

this._subscribed = subscribeHistoryStatesTimeWindow(
this.hass!,
Expand Down
2 changes: 1 addition & 1 deletion src/panels/history/ha-panel-history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ class HaPanelHistory extends LitElement {
this._interval = undefined;
}
if (this._subscribed) {
this._subscribed.then((unsub) => unsub?.());
this._subscribed.then((unsub) => unsub?.()).catch(() => undefined);
this._subscribed = undefined;
}
}
Expand Down
58 changes: 53 additions & 5 deletions src/panels/lovelace/cards/hui-history-graph-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,23 +122,53 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
if (this.hasUpdated) {
this._subscribeHistory();
}
window.addEventListener("connection-status", this._handleConnectionStatus);
}

public disconnectedCallback() {
super.disconnectedCallback();
this._unsubscribeHistory();
window.removeEventListener(
"connection-status",
this._handleConnectionStatus
);
}

private _handleConnectionStatus = (ev: Event) => {
if ((ev as CustomEvent).detail === "connected") {
this._unsubscribeHistory();
this._error = undefined;
this._subscribeHistory();
}
};

private async _subscribeHistory() {
if (!isComponentLoaded(this.hass!.config, "history") || this._subscribed) {
return;
}

const { numeric_device_classes: sensorNumericDeviceClasses } =
await getSensorNumericDeviceClasses(this.hass!);
// Mark as subscribing before the first await to prevent re-entrant calls
const sentinel = Promise.resolve(undefined) as NonNullable<
typeof this._subscribed
>;
this._subscribed = sentinel;

let sensorNumericDeviceClasses: string[];
try {
({ numeric_device_classes: sensorNumericDeviceClasses } =
await getSensorNumericDeviceClasses(this.hass!));
} catch (_err) {
if (this._subscribed === sentinel) {
this._subscribed = undefined;
}
return;
}

if (!this.isConnected) {
return; // Skip subscribe if we already disconnected while awaiting
if (!this.isConnected || this._subscribed !== sentinel) {
if (this._subscribed === sentinel) {
this._subscribed = undefined;
}
return;
}

this._subscribed = subscribeHistoryStatesTimeWindow(
Expand Down Expand Up @@ -230,12 +260,27 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
private _unsubscribeHistory() {
clearInterval(this._interval);
if (this._subscribed) {
this._subscribed.then((unsub) => unsub?.());
this._subscribed.then((unsub) => unsub?.()).catch(() => undefined);
this._subscribed = undefined;
}
}

protected shouldUpdate(changedProps: PropertyValues): boolean {
// Allow update when components list changes so we can retry subscription
if (
!this._subscribed &&
!this._error &&
this._config &&
changedProps.has("hass")
) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
oldHass &&
oldHass.config.components !== this.hass!.config.components
) {
return true;
}
}
return (
hasConfigOrEntitiesChanged(this, changedProps) ||
changedProps.size > 1 ||
Expand Down Expand Up @@ -269,6 +314,9 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
) {
this._unsubscribeHistory();
this._subscribeHistory();
} else if (!this._subscribed && !this._error && changedProps.has("hass")) {
// Retry subscription when components become available after backend restart
this._subscribeHistory();
}
}

Expand Down
29 changes: 27 additions & 2 deletions src/panels/lovelace/cards/hui-map-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@ class HuiMapCard extends LitElement implements LovelaceCard {
return true;
}

// Allow update when components list changes so we can retry subscription
if (
!this._subscribed &&
!this._error &&
this._config &&
oldHass.config.components !== this.hass.config.components
) {
return true;
}

if (changedProps.has("_stateHistory")) {
return true;
}
Expand Down Expand Up @@ -329,13 +339,28 @@ class HuiMapCard extends LitElement implements LovelaceCard {
if (this.hasUpdated && this._configEntities?.length) {
this._subscribeHistory();
}
window.addEventListener("connection-status", this._handleConnectionStatus);
}

public disconnectedCallback() {
super.disconnectedCallback();
this._unsubscribeHistory();
window.removeEventListener(
"connection-status",
this._handleConnectionStatus
);
}

private _handleConnectionStatus = (ev: Event) => {
if ((ev as CustomEvent).detail === "connected") {
this._unsubscribeHistory();
this._error = undefined;
if (this._configEntities?.length) {
this._subscribeHistory();
}
}
};

private _subscribeHistory() {
if (
!isComponentLoaded(this.hass!.config, "history") ||
Expand Down Expand Up @@ -367,14 +392,14 @@ class HuiMapCard extends LitElement implements LovelaceCard {

private _unsubscribeHistory() {
if (this._subscribed) {
this._subscribed.then((unsub) => unsub?.());
this._subscribed.then((unsub) => unsub?.()).catch(() => undefined);
this._subscribed = undefined;
}
}

protected updated(changedProps: PropertyValues): void {
if (this._configEntities?.length) {
if (!this._subscribed || changedProps.has("_config")) {
if ((!this._subscribed && !this._error) || changedProps.has("_config")) {
this._unsubscribeHistory();
this._subscribeHistory();
}
Expand Down
36 changes: 27 additions & 9 deletions src/panels/lovelace/header-footer/hui-graph-header-footer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,26 @@ export class HuiGraphHeaderFooter
if (this.hasUpdated && this._config) {
this._subscribeHistory();
}
window.addEventListener("connection-status", this._handleConnectionStatus);
}

public disconnectedCallback() {
super.disconnectedCallback();
this._unsubscribeHistory();
window.removeEventListener(
"connection-status",
this._handleConnectionStatus
);
}

private _handleConnectionStatus = (ev: Event) => {
if ((ev as CustomEvent).detail === "connected") {
this._unsubscribeHistory();
this._error = undefined;
this._subscribeHistory();
}
};

private _subscribeHistory() {
if (
!isComponentLoaded(this.hass!.config, "history") ||
Expand Down Expand Up @@ -264,24 +277,29 @@ export class HuiGraphHeaderFooter
private _unsubscribeHistory() {
clearInterval(this._interval);
if (this._subscribed) {
this._subscribed.then((unsub) => unsub?.());
this._subscribed.then((unsub) => unsub?.()).catch(() => undefined);
this._subscribed = undefined;
}
this._history = undefined;
}

protected updated(changedProps: PropertyValues) {
if (!this._config || !this.hass || !changedProps.has("_config")) {
if (!this._config || !this.hass) {
return;
}

const oldConfig = changedProps.get("_config") as GraphHeaderFooterConfig;
if (
!oldConfig ||
!this._subscribed ||
oldConfig.entity !== this._config.entity
) {
this._unsubscribeHistory();
if (changedProps.has("_config")) {
const oldConfig = changedProps.get("_config") as GraphHeaderFooterConfig;
if (
!oldConfig ||
!this._subscribed ||
oldConfig.entity !== this._config.entity
) {
this._unsubscribeHistory();
this._subscribeHistory();
}
} else if (!this._subscribed && !this._error && changedProps.has("hass")) {
// Retry subscription when components become available after backend restart
this._subscribeHistory();
}
}
Expand Down
Loading