From f226fcb04c00b42923a411ad44673a04fdd02c3e Mon Sep 17 00:00:00 2001 From: Nick Mowen Date: Tue, 19 Dec 2023 08:02:54 -0700 Subject: [PATCH] Save timeline entries for api events --- frigate/events/external.py | 2 +- frigate/events/maintainer.py | 10 +++++++++ frigate/timeline.py | 39 ++++++++++++++++++++++++++++++++++ web/src/pages/History.tsx | 2 +- web/src/types/history.ts | 11 +++++++++- web/src/utils/timelineUtil.tsx | 19 ++++++++++++++++- 6 files changed, 79 insertions(+), 4 deletions(-) diff --git a/frigate/events/external.py b/frigate/events/external.py index b02aaeba5..9c99ef50c 100644 --- a/frigate/events/external.py +++ b/frigate/events/external.py @@ -52,7 +52,7 @@ class ExternalEventProcessor: ( EventTypeEnum.api, "new", - camera_config, + camera, { "id": event_id, "label": label, diff --git a/frigate/events/maintainer.py b/frigate/events/maintainer.py index 19bb44ef4..eadf888c9 100644 --- a/frigate/events/maintainer.py +++ b/frigate/events/maintainer.py @@ -109,6 +109,16 @@ class EventProcessor(threading.Thread): self.handle_object_detection(event_type, camera, event_data) elif source_type == EventTypeEnum.api: + self.timeline_queue.put( + ( + camera, + source_type, + event_type, + {}, + event_data, + ) + ) + self.handle_external_detection(event_type, event_data) # set an end_time on events without an end_time before exiting diff --git a/frigate/timeline.py b/frigate/timeline.py index 984dd3ae2..927fd6845 100644 --- a/frigate/timeline.py +++ b/frigate/timeline.py @@ -48,6 +48,8 @@ class TimelineProcessor(threading.Thread): self.handle_object_detection( camera, event_type, prev_event_data, event_data ) + elif input_type == EventTypeEnum.api: + self.handle_api_entry(camera, event_type, event_data) def insert_or_save( self, @@ -140,3 +142,40 @@ class TimelineProcessor(threading.Thread): if save: self.insert_or_save(timeline_entry, prev_event_data, event_data) + + def handle_api_entry( + self, + camera: str, + event_type: str, + event_data: dict[any, any], + ) -> bool: + if event_type != "new": + return False + + if event_data.get("type", "api") == "audio": + timeline_entry = { + Timeline.class_type: "heard", + Timeline.timestamp: event_data["start_time"], + Timeline.camera: camera, + Timeline.source: "audio", + Timeline.source_id: event_data["id"], + Timeline.data: { + "label": event_data["label"], + "sub_label": event_data.get("sub_label"), + }, + } + else: + timeline_entry = { + Timeline.class_type: "external", + Timeline.timestamp: event_data["start_time"], + Timeline.camera: camera, + Timeline.source: "api", + Timeline.source_id: event_data["id"], + Timeline.data: { + "label": event_data["label"], + "sub_label": event_data.get("sub_label"), + }, + } + + Timeline.insert(timeline_entry).execute() + return True diff --git a/web/src/pages/History.tsx b/web/src/pages/History.tsx index bc174a8ba..ec40a4db5 100644 --- a/web/src/pages/History.tsx +++ b/web/src/pages/History.tsx @@ -271,7 +271,7 @@ function History() { ); })} - {lastRow && } + {lastRow && !isDone && } ); } diff --git a/web/src/types/history.ts b/web/src/types/history.ts index a5e7fa8a7..a6681dac9 100644 --- a/web/src/types/history.ts +++ b/web/src/types/history.ts @@ -27,7 +27,16 @@ type Timeline = { data: { [key: string]: any; }; - class_type: string; + class_type: + | "visible" + | "gone" + | "sub_label" + | "entered_zone" + | "attribute" + | "active" + | "stationary" + | "heard" + | "external"; source_id: string; source: string; }; diff --git a/web/src/utils/timelineUtil.tsx b/web/src/utils/timelineUtil.tsx index f85be6ad8..5f9333291 100644 --- a/web/src/utils/timelineUtil.tsx +++ b/web/src/utils/timelineUtil.tsx @@ -1,4 +1,11 @@ -import { LuCircle, LuPlay, LuPlayCircle, LuTruck } from "react-icons/lu"; +import { + LuCircle, + LuCircleDot, + LuEar, + LuPlay, + LuPlayCircle, + LuTruck, +} from "react-icons/lu"; import { IoMdExit } from "react-icons/io"; import { MdFaceUnlock, @@ -33,7 +40,13 @@ export function getTimelineIcon(timelineItem: Timeline) { return ; case "car": return ; + default: + return ; } + case "heard": + return ; + case "external": + return ; } } @@ -76,5 +89,9 @@ export function getTimelineItemDescription(timelineItem: Timeline) { return `${timelineItem.data.label} recognized as ${timelineItem.data.sub_label}`; case "gone": return `${label} left`; + case "heard": + return `${label} heard`; + case "external": + return `${label} detected`; } }