Fix Frigate event schema break (top_score === null)

This commit is contained in:
Dermot Duffy
2023-05-06 11:46:30 -07:00
parent 23a69f007f
commit cb6a73c3f8
4 changed files with 188 additions and 5 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ const eventSchema = z.object({
label: z.string(),
sub_label: z.string().nullable(),
start_time: z.number(),
top_score: z.number(),
top_score: z.number().nullable(),
zones: z.string().array(),
retain_indefinitely: z.boolean().optional(),
});
+4 -4
View File
@@ -14,11 +14,11 @@ export const getEventTitle = (event: FrigateEvent): string => {
? event.end_time - event.start_time
: Date.now() / 1000 - event.start_time,
);
const score = event.top_score !== null ? ` ${Math.round(event.top_score * 100)}%` : '';
return `${formatDateAndTime(
utcToZonedTime(event.start_time * 1000, localTimezone),
)} [${durationSeconds}s, ${prettifyTitle(event.label)} ${Math.round(
event.top_score * 100,
)}%]`;
)} [${durationSeconds}s, ${prettifyTitle(event.label)}${score}]`;
};
export const getRecordingTitle = (
@@ -93,5 +93,5 @@ export const getRecordingID = (
// can be dedup'd from this id.
return `${cameraConfig.frigate?.client_id ?? ''}/${
cameraConfig.frigate.camera_name ?? ''
}/${recording.startTime.getTime()}/${recording.endTime.getTime()}}`;
}/${recording.startTime.getTime()}/${recording.endTime.getTime()}`;
};
+154
View File
@@ -0,0 +1,154 @@
import sub from 'date-fns/sub';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { mock } from 'vitest-mock-extended';
import {
getEventMediaContentID,
getEventThumbnailURL,
getEventTitle,
getRecordingID,
getRecordingMediaContentID,
getRecordingTitle,
} from '../../../src/camera-manager/frigate/util';
import { CameraConfig } from '../../../src/types';
import {
createCameraConfig,
createFrigateEvent,
createFrigateRecording,
} from '../../test-utils';
describe('getEventTitle', () => {
afterEach(() => {
vi.useRealTimers();
});
it('should get finished event title', () => {
expect(
getEventTitle(
createFrigateEvent({
start_time: 1683395000,
end_time: 1683397124,
top_score: 0.841796875,
label: 'person',
}),
),
).toBe('2023-05-06 10:43 [2124s, Person 84%]');
});
it('should get in-progress event title', () => {
const baseTime = new Date('2023-04-29T14:25');
vi.useFakeTimers();
vi.setSystemTime(baseTime);
const startTime = sub(baseTime, { seconds: 60 });
expect(
getEventTitle(
createFrigateEvent({
start_time: startTime.getTime() / 1000,
end_time: null,
top_score: 0.841796875,
label: 'person',
}),
),
).toBe('2023-04-29 14:24 [60s, Person 84%]');
});
it('should get scoreless event title', () => {
expect(
getEventTitle(
createFrigateEvent({
start_time: 1683395000,
end_time: 1683397124,
top_score: null,
label: 'person',
}),
),
).toBe('2023-05-06 10:43 [2124s, Person]');
});
});
describe('getRecordingTitle', () => {
it('should get recording title', () => {
expect(
getRecordingTitle(
'Kitchen',
createFrigateRecording({
startTime: new Date('2023-04-29T14:00:00'),
}),
),
).toBe('Kitchen 2023-04-29 14:00');
});
});
describe('getEventThumbnailURL', () => {
it('should get thumbnail URL', () => {
expect(
getEventThumbnailURL(
'clientid',
createFrigateEvent({
id: '1683396875.643998-hmzrh5',
}),
),
).toBe('/api/frigate/clientid/thumbnail/1683396875.643998-hmzrh5');
});
});
describe('getEventMediaContentID', () => {
it('should get event content ID', () => {
expect(
getEventMediaContentID(
'clientid',
'kitchen',
createFrigateEvent({
id: '1683396875.643998-hmzrh5',
}),
'clips',
),
).toBe(
'media-source://frigate/clientid/event/clips/kitchen/1683396875.643998-hmzrh5',
);
});
});
describe('getRecordingMediaContentID', () => {
it('should get recording content ID', () => {
expect(
getRecordingMediaContentID(
'clientid',
'kitchen',
createFrigateRecording({
startTime: new Date('2023-04-29T14:00:00'),
}),
),
).toBe('media-source://frigate/clientid/recordings/kitchen/2023-04-29/14');
});
});
describe('getRecordingID', () => {
it('should get recording ID', () => {
expect(
getRecordingID(
createCameraConfig({
frigate: {
client_id: 'unique_client_id',
camera_name: 'kitchen',
},
}),
createFrigateRecording({
startTime: new Date('2023-04-29T14:00:00'),
endTime: new Date('2023-04-29T14:59:59'),
}),
),
).toBe('unique_client_id/kitchen/1682802000000/1682805599000');
});
it('should get recording ID without client_id or camera_name', () => {
// Note: This path is defended against in the code but should not happen in
// practice as this would be a malformed (not-zod-parsed) camera config.
const cameraConfig = mock<CameraConfig>();
expect(
getRecordingID(
cameraConfig,
createFrigateRecording({
startTime: new Date('2023-04-29T14:00:00'),
endTime: new Date('2023-04-29T14:59:59'),
}),
),
).toBe('//1682802000000/1682805599000');
});
});
+29
View File
@@ -1,6 +1,7 @@
import { HomeAssistant } from 'custom-card-helpers';
import { HassEntities, HassEntity } from 'home-assistant-js-websocket';
import { mock } from 'vitest-mock-extended';
import { FrigateEvent, FrigateRecording } from '../src/camera-manager/frigate/types';
import {
CameraConfig,
FrigateCardCondition,
@@ -60,3 +61,31 @@ export const createStateEntity = (entity?: Partial<HassEntity>): HassEntity => {
},
};
};
export const createFrigateEvent = (event?: Partial<FrigateEvent>) => {
return {
camera: 'camera',
end_time: 1683397124,
false_positive: false,
has_clip: true,
has_snapshot: true,
id: '1683396875.643998-hmzrh5',
label: 'person',
sub_label: null,
start_time: 1683395000,
top_score: 0.841796875,
zones: [],
retain_indefinitely: false,
...event,
};
};
export const createFrigateRecording = (recording?: Partial<FrigateRecording>) => {
return {
cameraID: 'cameraID',
startTime: new Date('2023-04-29T14:00:00'),
endTime: new Date('2023-04-29T14:59:59'),
events: 42,
...recording,
};
};