Fix issues with 2-way audio for cameras that support only a single stream.
This commit is contained in:
@@ -21,11 +21,19 @@ export class InitializationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isInitializedMandatory(): boolean {
|
public isInitializedMandatory(): boolean {
|
||||||
|
const config = this._api.getConfigManager().getConfig();
|
||||||
|
if (!config) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this._initializer.isInitializedMultiple([
|
this._initializer.isInitializedMultiple([
|
||||||
InitializationAspect.LANGUAGES,
|
InitializationAspect.LANGUAGES,
|
||||||
InitializationAspect.SIDE_LOAD_ELEMENTS,
|
InitializationAspect.SIDE_LOAD_ELEMENTS,
|
||||||
InitializationAspect.CAMERAS,
|
InitializationAspect.CAMERAS,
|
||||||
|
...(config.live.microphone.always_connected
|
||||||
|
? [InitializationAspect.MICROPHONE_CONNECT]
|
||||||
|
: []),
|
||||||
]) &&
|
]) &&
|
||||||
// If there's no view, re-initialize (e.g. config changes).
|
// If there's no view, re-initialize (e.g. config changes).
|
||||||
this._api.getViewManager().hasView()
|
this._api.getViewManager().hasView()
|
||||||
@@ -54,15 +62,25 @@ export class InitializationManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._api.getConfigManager().hasConfig()) {
|
const config = this._api.getConfigManager().getConfig();
|
||||||
|
if (!config) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(await this._initializer.initializeIfNecessary(
|
!(await this._initializer.initializeMultipleIfNecessary({
|
||||||
InitializationAspect.CAMERAS,
|
[InitializationAspect.CAMERAS]: async () =>
|
||||||
async () => await this._api.getCameraManager().initializeCamerasFromConfig(),
|
await this._api.getCameraManager().initializeCamerasFromConfig(),
|
||||||
))
|
|
||||||
|
// Connecting the microphone (if configured) is considered mandatory to
|
||||||
|
// avoid issues with some cameras that only allow 2-way audio on the
|
||||||
|
// first stream initialized. See:
|
||||||
|
// https://github.com/dermotduffy/frigate-hass-card/issues/1235
|
||||||
|
...(config.live.microphone.always_connected && {
|
||||||
|
[InitializationAspect.MICROPHONE_CONNECT]: async () =>
|
||||||
|
await this._api.getMicrophoneManager().connect(),
|
||||||
|
}),
|
||||||
|
}))
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -104,9 +122,6 @@ export class InitializationManager {
|
|||||||
...(config.menu.buttons.media_player.enabled
|
...(config.menu.buttons.media_player.enabled
|
||||||
? [InitializationAspect.MEDIA_PLAYERS]
|
? [InitializationAspect.MEDIA_PLAYERS]
|
||||||
: []),
|
: []),
|
||||||
...(config.live.microphone.always_connected
|
|
||||||
? [InitializationAspect.MICROPHONE_CONNECT]
|
|
||||||
: []),
|
|
||||||
])
|
])
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
@@ -118,10 +133,6 @@ export class InitializationManager {
|
|||||||
[InitializationAspect.MEDIA_PLAYERS]: async () =>
|
[InitializationAspect.MEDIA_PLAYERS]: async () =>
|
||||||
await this._api.getMediaPlayerManager().initialize(),
|
await this._api.getMediaPlayerManager().initialize(),
|
||||||
}),
|
}),
|
||||||
...(config.live.microphone.always_connected && {
|
|
||||||
[InitializationAspect.MICROPHONE_CONNECT]: async () =>
|
|
||||||
await this._api.getMicrophoneManager().connect(),
|
|
||||||
}),
|
|
||||||
}))
|
}))
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -19,11 +19,20 @@ describe('InitializationManager', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('should correctly determine when mandatory initialization is required', () => {
|
describe('should correctly determine when mandatory initialization is required', () => {
|
||||||
|
it('without config', () => {
|
||||||
|
const api = createCardAPI();
|
||||||
|
const initializer = mock<Initializer>();
|
||||||
|
const manager = new InitializationManager(api, initializer);
|
||||||
|
|
||||||
|
expect(manager.isInitializedMandatory()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
it('without aspects', () => {
|
it('without aspects', () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
const initializer = mock<Initializer>();
|
const initializer = mock<Initializer>();
|
||||||
const manager = new InitializationManager(api, initializer);
|
const manager = new InitializationManager(api, initializer);
|
||||||
|
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
initializer.isInitializedMultiple.mockReturnValue(false);
|
initializer.isInitializedMultiple.mockReturnValue(false);
|
||||||
|
|
||||||
expect(manager.isInitializedMandatory()).toBeFalsy();
|
expect(manager.isInitializedMandatory()).toBeFalsy();
|
||||||
@@ -34,6 +43,7 @@ describe('InitializationManager', () => {
|
|||||||
const initializer = mock<Initializer>();
|
const initializer = mock<Initializer>();
|
||||||
const manager = new InitializationManager(api, initializer);
|
const manager = new InitializationManager(api, initializer);
|
||||||
|
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
initializer.isInitializedMultiple.mockReturnValue(true);
|
initializer.isInitializedMultiple.mockReturnValue(true);
|
||||||
|
|
||||||
expect(manager.isInitializedMandatory()).toBeFalsy();
|
expect(manager.isInitializedMandatory()).toBeFalsy();
|
||||||
@@ -44,6 +54,27 @@ describe('InitializationManager', () => {
|
|||||||
const initializer = mock<Initializer>();
|
const initializer = mock<Initializer>();
|
||||||
const manager = new InitializationManager(api, initializer);
|
const manager = new InitializationManager(api, initializer);
|
||||||
|
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
|
initializer.isInitializedMultiple.mockReturnValue(true);
|
||||||
|
vi.mocked(api.getViewManager().hasView).mockReturnValue(true);
|
||||||
|
|
||||||
|
expect(manager.isInitializedMandatory()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with microphone if configured', () => {
|
||||||
|
const api = createCardAPI();
|
||||||
|
const initializer = mock<Initializer>();
|
||||||
|
const manager = new InitializationManager(api, initializer);
|
||||||
|
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
|
createConfig({
|
||||||
|
live: {
|
||||||
|
microphone: {
|
||||||
|
always_connected: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
initializer.isInitializedMultiple.mockReturnValue(true);
|
initializer.isInitializedMultiple.mockReturnValue(true);
|
||||||
vi.mocked(api.getViewManager().hasView).mockReturnValue(true);
|
vi.mocked(api.getViewManager().hasView).mockReturnValue(true);
|
||||||
|
|
||||||
@@ -67,7 +98,7 @@ describe('InitializationManager', () => {
|
|||||||
it('successfully', async () => {
|
it('successfully', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
vi.mocked(api.getConfigManager().hasConfig).mockReturnValue(true);
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
vi.mocked(api.getMessageManager().hasMessage).mockReturnValue(false);
|
vi.mocked(api.getMessageManager().hasMessage).mockReturnValue(false);
|
||||||
vi.mocked(api.getQueryStringManager().hasViewRelatedActions).mockReturnValue(
|
vi.mocked(api.getQueryStringManager().hasViewRelatedActions).mockReturnValue(
|
||||||
false,
|
false,
|
||||||
@@ -80,12 +111,31 @@ describe('InitializationManager', () => {
|
|||||||
expect(sideLoadHomeAssistantElements).toBeCalled();
|
expect(sideLoadHomeAssistantElements).toBeCalled();
|
||||||
expect(api.getCameraManager().initializeCamerasFromConfig).toBeCalled();
|
expect(api.getCameraManager().initializeCamerasFromConfig).toBeCalled();
|
||||||
expect(api.getViewManager().setViewDefault).toBeCalled();
|
expect(api.getViewManager().setViewDefault).toBeCalled();
|
||||||
|
expect(api.getMicrophoneManager().connect).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('successfully with microphone if configured', async () => {
|
||||||
|
const api = createCardAPI();
|
||||||
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
|
createConfig({
|
||||||
|
live: {
|
||||||
|
microphone: {
|
||||||
|
always_connected: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const manager = new InitializationManager(api);
|
||||||
|
|
||||||
|
expect(await manager.initializeMandatory()).toBeTruthy();
|
||||||
|
expect(api.getMicrophoneManager().connect).toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('successfully with querystring view', async () => {
|
it('successfully with querystring view', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
vi.mocked(api.getConfigManager().hasConfig).mockReturnValue(true);
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
vi.mocked(api.getMessageManager().hasMessage).mockReturnValue(false);
|
vi.mocked(api.getMessageManager().hasMessage).mockReturnValue(false);
|
||||||
vi.mocked(api.getQueryStringManager().hasViewRelatedActions).mockReturnValue(true);
|
vi.mocked(api.getQueryStringManager().hasViewRelatedActions).mockReturnValue(true);
|
||||||
const manager = new InitializationManager(api);
|
const manager = new InitializationManager(api);
|
||||||
@@ -98,7 +148,7 @@ describe('InitializationManager', () => {
|
|||||||
it('with message set during initialization', async () => {
|
it('with message set during initialization', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
vi.mocked(api.getConfigManager().hasConfig).mockReturnValue(true);
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
vi.mocked(api.getMessageManager().hasMessage).mockReturnValue(true);
|
vi.mocked(api.getMessageManager().hasMessage).mockReturnValue(true);
|
||||||
vi.mocked(api.getQueryStringManager().hasViewRelatedActions).mockReturnValue(
|
vi.mocked(api.getQueryStringManager().hasViewRelatedActions).mockReturnValue(
|
||||||
false,
|
false,
|
||||||
@@ -124,12 +174,13 @@ describe('InitializationManager', () => {
|
|||||||
it('with cameras in progress', async () => {
|
it('with cameras in progress', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
vi.mocked(api.getConfigManager().hasConfig).mockReturnValue(true);
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
|
|
||||||
const initializer = mock<Initializer>();
|
const initializer = mock<Initializer>();
|
||||||
const manager = new InitializationManager(api, initializer);
|
const manager = new InitializationManager(api, initializer);
|
||||||
initializer.initializeMultipleIfNecessary.mockResolvedValue(true);
|
initializer.initializeMultipleIfNecessary
|
||||||
initializer.initializeIfNecessary.mockResolvedValue(false);
|
.mockResolvedValueOnce(true)
|
||||||
|
.mockResolvedValueOnce(false);
|
||||||
|
|
||||||
expect(await manager.initializeMandatory()).toBeFalsy();
|
expect(await manager.initializeMandatory()).toBeFalsy();
|
||||||
});
|
});
|
||||||
@@ -154,17 +205,11 @@ describe('InitializationManager', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
live: {
|
|
||||||
microphone: {
|
|
||||||
always_connected: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(await manager.initializeBackgroundIfNecessary()).toBeTruthy();
|
expect(await manager.initializeBackgroundIfNecessary()).toBeTruthy();
|
||||||
expect(api.getMediaPlayerManager().initialize).not.toBeCalled();
|
expect(api.getMediaPlayerManager().initialize).not.toBeCalled();
|
||||||
expect(api.getMicrophoneManager().connect).not.toBeCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('successfully with all inititalizers', async () => {
|
it('successfully with all inititalizers', async () => {
|
||||||
@@ -180,21 +225,15 @@ describe('InitializationManager', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
live: {
|
|
||||||
microphone: {
|
|
||||||
always_connected: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(await manager.initializeBackgroundIfNecessary()).toBeTruthy();
|
expect(await manager.initializeBackgroundIfNecessary()).toBeTruthy();
|
||||||
expect(api.getMediaPlayerManager().initialize).toBeCalled();
|
expect(api.getMediaPlayerManager().initialize).toBeCalled();
|
||||||
expect(api.getMicrophoneManager().connect).toBeCalled();
|
|
||||||
expect(api.getCardElementManager().update).toBeCalled();
|
expect(api.getCardElementManager().update).toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with media player and microphone connect in progress', async () => {
|
it('with media player in progress', async () => {
|
||||||
const api = createCardAPI();
|
const api = createCardAPI();
|
||||||
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(createHASS());
|
||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
|
||||||
@@ -206,11 +245,6 @@ describe('InitializationManager', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
live: {
|
|
||||||
microphone: {
|
|
||||||
always_connected: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const initializer = mock<Initializer>();
|
const initializer = mock<Initializer>();
|
||||||
|
|||||||
+3
-3
@@ -10,10 +10,10 @@ export default defineConfig({
|
|||||||
// Thresholds will automatically be updated as coverage improves to avoid
|
// Thresholds will automatically be updated as coverage improves to avoid
|
||||||
// back-sliding.
|
// back-sliding.
|
||||||
thresholdAutoUpdate: true,
|
thresholdAutoUpdate: true,
|
||||||
statements: 72.73,
|
statements: 72.76,
|
||||||
branches: 61.93,
|
branches: 61.95,
|
||||||
functions: 74.01,
|
functions: 74.01,
|
||||||
lines: 72.63,
|
lines: 72.66,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user