fix: Improve 2-way audio detection (#2293)
- For Frigate cameras, you must be running at least [integration v5.12.0](https://github.com/blakeblackshear/frigate-hass-integration/releases/tag/v5.12.0). - Closes: #2191 Includes a significant refactor of cameras, and the introduction of a `2-way-audio` capability that is dynamically fetched from `go2rtc`. One gotcha is if you previously had a substream with 2-way audio, you may need to modify ```yaml - camera_entity: camera.foo capabilities: disable_except: - substream ``` ... to ... ```yaml - camera_entity: camera.foo capabilities: disable_except: - substream - 2-way-audio ```
This commit is contained in:
@@ -9,11 +9,10 @@ import { FrigateEvent, eventSchema } from '../../../src/camera-manager/frigate/t
|
||||
import { CameraManagerRequestCache } from '../../../src/camera-manager/types';
|
||||
import { StateWatcher } from '../../../src/card-controller/hass/state-watcher';
|
||||
import { CameraConfig } from '../../../src/config/schema/cameras';
|
||||
import { AdvancedCameraCardView } from '../../../src/config/schema/common/const';
|
||||
import { RawAdvancedCameraCardConfig } from '../../../src/config/types';
|
||||
import { ViewMedia, ViewMediaType } from '../../../src/view/item';
|
||||
import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock';
|
||||
import { TestViewMedia, createCameraConfig, createHASS } from '../../test-utils';
|
||||
import { createCameraConfig, createHASS } from '../../test-utils';
|
||||
|
||||
const createEngine = (): FrigateCameraManagerEngine => {
|
||||
return new FrigateCameraManagerEngine(
|
||||
@@ -141,265 +140,3 @@ describe('getMediaDownloadPath', () => {
|
||||
expect(endpoint).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCameraEndpoints', () => {
|
||||
it('should get basic endpoints', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(createFrigateCameraConfig());
|
||||
|
||||
expect(endpoints).toEqual({
|
||||
go2rtc: {
|
||||
endpoint: '/api/frigate/frigate/mse/api/ws?src=camera-1',
|
||||
sign: true,
|
||||
},
|
||||
jsmpeg: {
|
||||
endpoint: '/api/frigate/frigate/jsmpeg/camera-1',
|
||||
sign: true,
|
||||
},
|
||||
webrtcCard: {
|
||||
endpoint: 'camera.office',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('should get overridden go2rtc url', () => {
|
||||
it('when local HA path', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createFrigateCameraConfig({
|
||||
go2rtc: {
|
||||
url: '/local/path',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(endpoints).toEqual(
|
||||
expect.objectContaining({
|
||||
go2rtc: {
|
||||
endpoint: '/local/path/api/ws?src=camera-1',
|
||||
sign: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('when remote', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createFrigateCameraConfig({
|
||||
go2rtc: {
|
||||
url: 'https://my.custom.go2rtc',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(endpoints).toEqual(
|
||||
expect.objectContaining({
|
||||
go2rtc: {
|
||||
endpoint: 'https://my.custom.go2rtc/api/ws?src=camera-1',
|
||||
sign: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not set webrtc_card endpoint without camera name', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(createCameraConfig());
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
webrtcCard: expect.anything(),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
describe('should include UI endpoint', () => {
|
||||
it('with basic url', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('with camera name', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
camera_name: 'my-camera',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate/cameras/my-camera',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
describe('with event media type', () => {
|
||||
it.each([[ViewMediaType.Clip], [ViewMediaType.Snapshot]])(
|
||||
'%s',
|
||||
(mediaType: ViewMediaType) => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
camera_name: 'my-camera',
|
||||
},
|
||||
}),
|
||||
{
|
||||
media: new TestViewMedia({ mediaType: mediaType }),
|
||||
},
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate/events?camera=my-camera',
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('with recording media type', () => {
|
||||
it('with start time', () => {
|
||||
const startTime = new Date('2023-10-07T16:42:00');
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
camera_name: 'my-camera',
|
||||
},
|
||||
}),
|
||||
{
|
||||
media: new TestViewMedia({
|
||||
mediaType: ViewMediaType.Recording,
|
||||
startTime: startTime,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate/recording/my-camera/2023-10-07/16',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('without start time', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
camera_name: 'my-camera',
|
||||
},
|
||||
}),
|
||||
{
|
||||
media: new TestViewMedia({ mediaType: ViewMediaType.Recording }),
|
||||
},
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate/recording/my-camera/',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with view', () => {
|
||||
it('live', () => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
camera_name: 'my-camera',
|
||||
},
|
||||
}),
|
||||
{
|
||||
view: 'live',
|
||||
},
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate/cameras/my-camera',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['clip' as const],
|
||||
['clips' as const],
|
||||
['snapshot' as const],
|
||||
['snapshots' as const],
|
||||
])('%s', (viewName: AdvancedCameraCardView) => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
camera_name: 'my-camera',
|
||||
},
|
||||
}),
|
||||
{
|
||||
view: viewName,
|
||||
},
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate/events?camera=my-camera',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it.each([['recording' as const], ['recordings' as const]])(
|
||||
'%s',
|
||||
(viewName: AdvancedCameraCardView) => {
|
||||
const endpoints = createEngine().getCameraEndpoints(
|
||||
createCameraConfig({
|
||||
frigate: {
|
||||
url: 'http://my.frigate',
|
||||
camera_name: 'my-camera',
|
||||
},
|
||||
}),
|
||||
{
|
||||
view: viewName,
|
||||
},
|
||||
);
|
||||
|
||||
expect(endpoints).not.toEqual(
|
||||
expect.objectContaining({
|
||||
ui: {
|
||||
url: 'http://my.frigate/recording/my-camera/',
|
||||
},
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user