From 03741727c4b35179c263ef4e8f0f92b885616268 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Tue, 3 Jun 2025 08:35:19 -0600 Subject: [PATCH] Build out route --- web/src/App.tsx | 2 ++ web/src/hooks/use-navigation.ts | 11 +++++- web/src/pages/ClassificationModel.tsx | 9 ++--- web/src/types/frigateConfig.ts | 20 +++++++++++ .../classification/ModelSelectionView.tsx | 35 ++++++++++++++++++- 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/web/src/App.tsx b/web/src/App.tsx index d3edbc3a2..cd7906e97 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -24,6 +24,7 @@ const System = lazy(() => import("@/pages/System")); const Settings = lazy(() => import("@/pages/Settings")); const UIPlayground = lazy(() => import("@/pages/UIPlayground")); const FaceLibrary = lazy(() => import("@/pages/FaceLibrary")); +const Classification = lazy(() => import("@/pages/ClassificationModel")); const Logs = lazy(() => import("@/pages/Logs")); const AccessDenied = lazy(() => import("@/pages/AccessDenied")); @@ -76,6 +77,7 @@ function DefaultAppView() { } /> } /> } /> + } /> } /> } /> diff --git a/web/src/hooks/use-navigation.ts b/web/src/hooks/use-navigation.ts index 41ec7227f..d9bd6f6a4 100644 --- a/web/src/hooks/use-navigation.ts +++ b/web/src/hooks/use-navigation.ts @@ -6,7 +6,7 @@ import { isDesktop } from "react-device-detect"; import { FaCompactDisc, FaVideo } from "react-icons/fa"; import { IoSearch } from "react-icons/io5"; import { LuConstruction } from "react-icons/lu"; -import { MdVideoLibrary } from "react-icons/md"; +import { MdCategory, MdVideoLibrary } from "react-icons/md"; import { TbFaceId } from "react-icons/tb"; import useSWR from "swr"; @@ -16,6 +16,7 @@ export const ID_EXPLORE = 3; export const ID_EXPORT = 4; export const ID_PLAYGROUND = 5; export const ID_FACE_LIBRARY = 6; +export const ID_CLASSIFICATION = 7; export default function useNavigation( variant: "primary" | "secondary" = "primary", @@ -71,6 +72,14 @@ export default function useNavigation( url: "/faces", enabled: isDesktop && config?.face_recognition.enabled, }, + { + id: ID_CLASSIFICATION, + variant, + icon: MdCategory, + title: "menu.classification", + url: "/classification", + enabled: isDesktop, + }, ] as NavData[], [config?.face_recognition?.enabled, variant], ); diff --git a/web/src/pages/ClassificationModel.tsx b/web/src/pages/ClassificationModel.tsx index 55eb3e27d..66a213243 100644 --- a/web/src/pages/ClassificationModel.tsx +++ b/web/src/pages/ClassificationModel.tsx @@ -1,17 +1,12 @@ -import { FrigateConfig } from "@/types/frigateConfig"; +import { CustomClassificationModelConfig } from "@/types/frigateConfig"; import ModelSelectionView from "@/views/classification/ModelSelectionView"; import ModelTrainingView from "@/views/classification/ModelTrainingView"; import { useState } from "react"; -import useSWR from "swr"; export default function ClassificationModelPage() { - const { data: config } = useSWR("config", { - revalidateOnFocus: false, - }); - // training - const [model, setModel] = useState(); + const [model, setModel] = useState(); if (model == undefined) { return ; diff --git a/web/src/types/frigateConfig.ts b/web/src/types/frigateConfig.ts index cf2bf1476..3ccc5b06d 100644 --- a/web/src/types/frigateConfig.ts +++ b/web/src/types/frigateConfig.ts @@ -279,6 +279,23 @@ export type CameraStreamingSettings = { volume: number; }; +export type CustomClassificationModelConfig = { + enabled: boolean; + name: string; + object_config: null | { + objects: string[]; + }; + state_config: null | { + cameras: { + [cameraName: string]: { + crop: [number, number, number, number]; + threshold: number; + }; + }; + motion: boolean; + }; +}; + export type GroupStreamingSettings = { [cameraName: string]: CameraStreamingSettings; }; @@ -316,6 +333,9 @@ export interface FrigateConfig { enabled: boolean; threshold: number; }; + custom: { + [modelKey: string]: CustomClassificationModelConfig; + }; }; database: { diff --git a/web/src/views/classification/ModelSelectionView.tsx b/web/src/views/classification/ModelSelectionView.tsx index eaf092fa7..557373a45 100644 --- a/web/src/views/classification/ModelSelectionView.tsx +++ b/web/src/views/classification/ModelSelectionView.tsx @@ -1 +1,34 @@ -export default function ModelSelectionView() {} +import ActivityIndicator from "@/components/indicators/activity-indicator"; +import { FrigateConfig } from "@/types/frigateConfig"; +import { useMemo } from "react"; +import useSWR from "swr"; + +export default function ModelSelectionView() { + const { data: config } = useSWR("config", { + revalidateOnFocus: false, + }); + + const classificationConfigs = useMemo(() => { + if (!config) { + return []; + } + + return Object.values(config.classification.custom); + }, [config]); + + if (!config) { + return ; + } + + if (classificationConfigs.length == 0) { + return
You need to setup a custom model configuration.
; + } + + return ( +
+ {classificationConfigs.map((config) => ( +
{config.name}
+ ))} +
+ ); +}