feat: Allow 2-way audio detection to be skipped and timed (#2355)
- For #2313
This commit is contained in:
@@ -76,6 +76,10 @@ describe('Camera', () => {
|
||||
});
|
||||
|
||||
describe('initialize', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should initialize and destroy', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
@@ -123,6 +127,7 @@ describe('Camera', () => {
|
||||
expect(liveProviderSupports2WayAudio).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
2,
|
||||
{
|
||||
endpoint:
|
||||
'http://go2rtc/api/streams?src=stream&video=all&audio=allµphone',
|
||||
@@ -154,6 +159,171 @@ describe('Camera', () => {
|
||||
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(false);
|
||||
});
|
||||
|
||||
it('should pass camera go2rtc metadata timeout', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
go2rtc: {
|
||||
url: 'http://go2rtc',
|
||||
stream: 'stream',
|
||||
metadata_fetch_timeout_seconds: 20,
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
|
||||
vi.mocked(liveProviderSupports2WayAudio).mockResolvedValue(true);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
20,
|
||||
expect.anything(),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should force 2-way-audio capability true without metadata fetch', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
capabilities: {
|
||||
force: ['2-way-audio'],
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).not.toHaveBeenCalled();
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(true);
|
||||
});
|
||||
|
||||
it('should prefer disable over force rules', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
capabilities: {
|
||||
disable: ['2-way-audio'],
|
||||
force: ['2-way-audio'],
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).not.toHaveBeenCalled();
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(false);
|
||||
});
|
||||
|
||||
it('should prefer disable_except over force rules', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
capabilities: {
|
||||
disable_except: ['substream'],
|
||||
force: ['2-way-audio'],
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).not.toHaveBeenCalled();
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(false);
|
||||
});
|
||||
|
||||
it('should not fetch metadata when 2-way-audio is disabled', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
capabilities: {
|
||||
disable: ['2-way-audio'],
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).not.toHaveBeenCalled();
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(false);
|
||||
});
|
||||
|
||||
it('should not fetch metadata when disable_except excludes 2-way-audio', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
capabilities: {
|
||||
disable_except: ['substream'],
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).not.toHaveBeenCalled();
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(false);
|
||||
});
|
||||
|
||||
it('should fetch metadata when disable_except includes 2-way-audio', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
capabilities: {
|
||||
disable_except: ['substream', '2-way-audio'],
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
vi.mocked(liveProviderSupports2WayAudio).mockResolvedValue(true);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).toHaveBeenCalled();
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(true);
|
||||
});
|
||||
|
||||
it('should fetch metadata when disable_except is empty', async () => {
|
||||
const camera = new Camera(
|
||||
createCameraConfig({
|
||||
capabilities: {
|
||||
disable_except: [],
|
||||
},
|
||||
}),
|
||||
new GenericCameraManagerEngine(mock<StateWatcherSubscriptionInterface>()),
|
||||
);
|
||||
vi.mocked(liveProviderSupports2WayAudio).mockResolvedValue(true);
|
||||
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
stateWatcher: mock<StateWatcherSubscriptionInterface>(),
|
||||
});
|
||||
|
||||
expect(liveProviderSupports2WayAudio).toHaveBeenCalled();
|
||||
expect(camera.getCapabilities()?.has('2-way-audio')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should handle trigger state changes', () => {
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('supports2WayAudio', () => {
|
||||
});
|
||||
|
||||
it('should return false if no endpoint provided', async () => {
|
||||
expect(await supports2WayAudio(hass, null)).toBe(false);
|
||||
expect(await supports2WayAudio(hass, 2, null)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false if fetch fails', async () => {
|
||||
@@ -26,7 +26,7 @@ describe('supports2WayAudio', () => {
|
||||
vi.mocked(homeAssistantSignAndFetch).mockRejectedValue(new Error('fetch error'));
|
||||
|
||||
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const result = await supports2WayAudio(hass, endpoint);
|
||||
const result = await supports2WayAudio(hass, 2, endpoint);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(spy).toHaveBeenCalledWith('fetch error');
|
||||
@@ -37,7 +37,35 @@ describe('supports2WayAudio', () => {
|
||||
vi.mocked(createProxiedEndpointIfNecessary).mockResolvedValue(endpoint);
|
||||
vi.mocked(homeAssistantSignAndFetch).mockResolvedValue({ producers: undefined });
|
||||
|
||||
expect(await supports2WayAudio(hass, endpoint)).toBe(false);
|
||||
expect(await supports2WayAudio(hass, 2, endpoint)).toBe(false);
|
||||
});
|
||||
|
||||
it('should use default metadata fetch timeout', async () => {
|
||||
vi.mocked(createProxiedEndpointIfNecessary).mockResolvedValue(endpoint);
|
||||
vi.mocked(homeAssistantSignAndFetch).mockResolvedValue({ producers: [] });
|
||||
|
||||
await supports2WayAudio(hass, 2, endpoint);
|
||||
|
||||
expect(homeAssistantSignAndFetch).toHaveBeenCalledWith(
|
||||
hass,
|
||||
endpoint,
|
||||
expect.anything(),
|
||||
{ timeoutSeconds: 2 },
|
||||
);
|
||||
});
|
||||
|
||||
it('should use custom metadata fetch timeout when provided', async () => {
|
||||
vi.mocked(createProxiedEndpointIfNecessary).mockResolvedValue(endpoint);
|
||||
vi.mocked(homeAssistantSignAndFetch).mockResolvedValue({ producers: [] });
|
||||
|
||||
await supports2WayAudio(hass, 15, endpoint);
|
||||
|
||||
expect(homeAssistantSignAndFetch).toHaveBeenCalledWith(
|
||||
hass,
|
||||
endpoint,
|
||||
expect.anything(),
|
||||
{ timeoutSeconds: 15 },
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false if no producer supports audio', async () => {
|
||||
@@ -50,7 +78,7 @@ describe('supports2WayAudio', () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(await supports2WayAudio(hass, endpoint)).toBe(false);
|
||||
expect(await supports2WayAudio(hass, 2, endpoint)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if producer supports audio and sendonly', async () => {
|
||||
@@ -63,7 +91,7 @@ describe('supports2WayAudio', () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(await supports2WayAudio(hass, endpoint)).toBe(true);
|
||||
expect(await supports2WayAudio(hass, 2, endpoint)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if producer supports audio and sendrecv', async () => {
|
||||
@@ -76,7 +104,7 @@ describe('supports2WayAudio', () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(await supports2WayAudio(hass, endpoint)).toBe(true);
|
||||
expect(await supports2WayAudio(hass, 2, endpoint)).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle missing medias in producer', async () => {
|
||||
@@ -89,6 +117,6 @@ describe('supports2WayAudio', () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(await supports2WayAudio(hass, endpoint)).toBe(false);
|
||||
expect(await supports2WayAudio(hass, 2, endpoint)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user