feat: add problem detection framework for common problems (#2412)

Introduces a ProblemManager that detects and surfaces actionable issues
(stale config, legacy frigate-hass-card resources, slow/failed streams)
via status bar indicators and notification popups with fix actions.
This commit is contained in:
Dermot Duffy
2026-03-13 20:00:59 -07:00
committed by GitHub
parent 95faddd9a0
commit ab683df5e8
46 changed files with 2147 additions and 166 deletions
+15 -15
View File
@@ -23,12 +23,12 @@ describe('ConditionsManager', () => {
it('should match named view change', () => {
const stateManager = new ConditionStateManager();
const manager = new ConditionsManager(
[{ condition: 'view' as const, views: ['foo'] }],
[{ condition: 'view' as const, views: ['live'] }],
stateManager,
);
expect(manager.getEvaluation().result).toBeFalsy();
stateManager.setState({ view: 'foo' });
stateManager.setState({ view: 'live' });
expect(manager.getEvaluation().result).toBeTruthy();
});
@@ -1211,13 +1211,13 @@ describe('ConditionsManager', () => {
camera: { to: 'camera-1' },
});
stateManager.setState({ view: 'view-1' });
stateManager.setState({ view: 'live' });
expect(manager.getEvaluation().result).toBeTruthy();
expect(manager.getEvaluation().triggerData).toEqual({
view: { to: 'view-1' },
view: { to: 'live' },
});
stateManager.setState({ camera: 'camera-2', view: 'view-2' });
stateManager.setState({ camera: 'camera-2', view: 'clip' });
expect(manager.getEvaluation().result).toBeTruthy();
expect(manager.getEvaluation().triggerData).toEqual({
camera: { to: 'camera-2', from: 'camera-1' },
@@ -1282,17 +1282,17 @@ describe('ConditionsManager', () => {
stateManager.setState({ camera: 'camera-1' });
expect(manager.getEvaluation().result).toBeFalsy();
stateManager.setState({ view: 'view-1' });
stateManager.setState({ view: 'live' });
expect(manager.getEvaluation().result).toBeFalsy();
stateManager.setState({ camera: 'camera-2', view: 'view-2' });
stateManager.setState({ camera: 'camera-2', view: 'clip' });
expect(manager.getEvaluation().result).toBeTruthy();
expect(manager.getEvaluation().triggerData).toEqual({
camera: { from: 'camera-1', to: 'camera-2' },
view: { from: 'view-1', to: 'view-2' },
view: { from: 'live', to: 'clip' },
});
stateManager.setState({ view: 'view-3' });
stateManager.setState({ view: 'snapshot' });
expect(manager.getEvaluation().result).toBeFalsy();
});
@@ -1404,26 +1404,26 @@ describe('ConditionsManager', () => {
it('with not call listeners when condition result does not change', () => {
const stateManager = new ConditionStateManager();
const manager = new ConditionsManager(
[{ condition: 'view' as const, views: ['foo'] }],
[{ condition: 'view' as const, views: ['live'] }],
stateManager,
);
const listener = vi.fn();
manager.addListener(listener);
stateManager.setState({ view: 'foo' });
stateManager.setState({ view: 'live' });
expect(listener).toBeCalledTimes(1);
stateManager.setState({ view: 'bar' });
stateManager.setState({ view: 'clip' });
expect(listener).toBeCalledTimes(2);
stateManager.setState({ view: 'bar' });
stateManager.setState({ view: 'clip' });
expect(listener).toBeCalledTimes(2);
stateManager.setState({ view: 'foo' });
stateManager.setState({ view: 'live' });
expect(listener).toBeCalledTimes(3);
stateManager.setState({ view: 'foo' });
stateManager.setState({ view: 'live' });
expect(listener).toBeCalledTimes(3);
});
});