Handle (invalid) null hass objects.

This commit is contained in:
Dermot Duffy
2023-10-05 19:54:52 -07:00
parent c817601f60
commit ee8f9e3fd1
6 changed files with 107 additions and 17 deletions
+15 -11
View File
@@ -16,7 +16,7 @@ export class HASSManager {
return this._hass;
}
public setHASS(hass: ExtendedHomeAssistant): void {
public setHASS(hass?: ExtendedHomeAssistant | null): void {
const getSelectedCameraConfig = (): CameraConfig | null => {
const view = this._api.getViewManager().getView();
const cameraManager = this._api.getCameraManager();
@@ -26,13 +26,8 @@ export class HASSManager {
: null;
};
const oldHass = this._hass;
this._hass = hass;
const selectedCamera = getSelectedCameraConfig();
if (hasHAConnectionStateChanged(oldHass, hass)) {
if (!this._hass?.connected) {
if (hasHAConnectionStateChanged(this._hass, hass)) {
if (!hass?.connected) {
this._api.getMessageManager().setMessageIfHigherPriority({
message: localize('error.reconnecting'),
icon: 'mdi:lan-disconnect',
@@ -40,9 +35,18 @@ export class HASSManager {
dotdotdot: true,
});
} else {
this._api.getViewManager().setViewDefault();
this._api.getMessageManager().resetType('connection');
}
} else if (
}
if (!hass) {
return;
}
const oldHass = this._hass;
this._hass = hass;
if (
// Home Assistant pumps a lot of updates through. Re-rendering the card is
// necessary at times (e.g. to update the 'clip' view as new clips
// arrive), but also is a jarring experience for the user (e.g. if they
@@ -52,7 +56,7 @@ export class HASSManager {
this._isAutomatedViewUpdateAllowed() &&
isHassDifferent(this._hass, oldHass, [
...(this._api.getConfigManager().getConfig()?.view.update_entities ?? []),
...(selectedCamera?.triggers.entities ?? []),
...(getSelectedCameraConfig()?.triggers.entities ?? []),
])
) {
// If entities being monitored have changed then reset the view to the
+7 -1
View File
@@ -1,4 +1,4 @@
import { FrigateCardError, MESSAGE_TYPE_PRIORITIES, Message } from '../types';
import { FrigateCardError, MESSAGE_TYPE_PRIORITIES, Message, MessageType } from '../types';
import { errorToConsole } from '../utils/basic';
import { CardMessageAPI } from './types';
@@ -31,6 +31,12 @@ export class MessageManager {
}
}
public resetType(type: MessageType): void {
if (this._message?.type === type) {
this.reset();
}
}
public setErrorIfHigherPriority(error: unknown): void {
// This object should accept unknown objects to be able to seamlessly
// process arguments to catch() which can only be unknown/any.
+1 -4
View File
@@ -375,8 +375,5 @@ export const hasHAConnectionStateChanged = (
oldHass: HomeAssistant | undefined | null,
newHass: HomeAssistant | undefined | null,
): boolean => {
return (
(!oldHass && !newHass?.connected) ||
(!!oldHass && oldHass.connected !== !!newHass?.connected)
);
return oldHass?.connected !== newHass?.connected;
};
+23 -1
View File
@@ -130,7 +130,29 @@ describe('HASSManager', () => {
const reconnectedHASS = createHASS();
manager.setHASS(reconnectedHASS);
expect(api.getViewManager().setViewDefault).toBeCalled();
expect(api.getMessageManager().resetType).toBeCalled();
});
it('hass is null', () => {
const api = createAPIWithoutMediaPlayers();
const manager = new HASSManager(api);
const connectedHASS = createHASS();
connectedHASS.connected = true;
manager.setHASS(connectedHASS);
manager.setHASS(null);
expect(api.getMessageManager().setMessageIfHigherPriority).toBeCalledWith(
expect.objectContaining({
message: 'Reconnecting',
icon: 'mdi:lan-disconnect',
type: 'connection',
dotdotdot: true,
}),
);
manager.setHASS(connectedHASS);
expect(api.getMessageManager().resetType).toBeCalled();
});
});
@@ -71,6 +71,22 @@ describe('MessageManager', () => {
expect(api.getCardElementManager().update).toBeCalled();
});
it('should reset message that matches type', () => {
const api = createCardAPI();
const manager = new MessageManager(api);
const message = createMessage({ type: 'connection' });
manager.setMessageIfHigherPriority(message);
expect(manager.getMessage()).toBe(message);
manager.resetType('error');
expect(manager.getMessage()).toBe(message);
manager.resetType('connection');
expect(manager.getMessage()).toBeNull();
expect(manager.hasMessage()).toBeFalsy();
});
it('should respect priority', () => {
const api = createCardAPI();
const manager = new MessageManager(api);
+45
View File
@@ -0,0 +1,45 @@
import { HomeAssistant } from 'custom-card-helpers';
import { describe, expect, it } from 'vitest';
import { hasHAConnectionStateChanged } from '../../../src/utils/ha/index.js';
import { createHASS } from '../../test-utils.js';
const createConnected = (connected: boolean): HomeAssistant => {
const hass = createHASS();
hass.connected = connected;
return hass;
};
describe('hasHAConnectionStateChanged', () => {
it('initially connected', () => {
expect(hasHAConnectionStateChanged(null, createConnected(true))).toBeTruthy();
});
it('initially disconnected', () => {
expect(hasHAConnectionStateChanged(null, createConnected(false))).toBeTruthy();
});
it('disconnected', () => {
expect(
hasHAConnectionStateChanged(createConnected(true), createConnected(false)),
).toBeTruthy();
});
it('disconnected via absence', () => {
expect(hasHAConnectionStateChanged(createConnected(true), null)).toBeTruthy();
});
it('connected', () => {
expect(
hasHAConnectionStateChanged(createConnected(false), createConnected(true)),
).toBeTruthy();
});
it('still disconnected', () => {
expect(
hasHAConnectionStateChanged(createConnected(false), createConnected(false)),
).toBeFalsy();
});
it('still connected', () => {
expect(
hasHAConnectionStateChanged(createConnected(true), createConnected(true)),
).toBeFalsy();
});
it('still absent', () => {
expect(hasHAConnectionStateChanged(null, null)).toBeFalsy();
});
});