From 06f47f262f0c45c800534f0a93e0a75ed41859e7 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Wed, 16 Oct 2024 07:27:36 -0600 Subject: [PATCH] Use config attribute map instead of hard coded (#14387) --- docker/main/install_deps.sh | 1 + .../components/settings/ObjectMaskEditPane.tsx | 17 +++++++++++++---- web/src/components/settings/ZoneEditPane.tsx | 17 +++++++++++++---- web/src/hooks/use-camera-activity.ts | 18 +++++++++++++++--- web/src/types/frigateConfig.ts | 8 -------- web/src/utils/iconUtil.tsx | 14 ++++++++++++++ 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/docker/main/install_deps.sh b/docker/main/install_deps.sh index c63c015d3..a61a8a9fb 100755 --- a/docker/main/install_deps.sh +++ b/docker/main/install_deps.sh @@ -1,3 +1,4 @@ + #!/bin/bash set -euxo pipefail diff --git a/web/src/components/settings/ObjectMaskEditPane.tsx b/web/src/components/settings/ObjectMaskEditPane.tsx index 2bfc5afac..cb8ed50ad 100644 --- a/web/src/components/settings/ObjectMaskEditPane.tsx +++ b/web/src/components/settings/ObjectMaskEditPane.tsx @@ -20,7 +20,7 @@ import { FormMessage, } from "@/components/ui/form"; import { useCallback, useEffect, useMemo } from "react"; -import { ATTRIBUTE_LABELS, FrigateConfig } from "@/types/frigateConfig"; +import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; @@ -37,6 +37,7 @@ import axios from "axios"; import { toast } from "sonner"; import { Toaster } from "../ui/sonner"; import ActivityIndicator from "../indicators/activity-indicator"; +import { getAttributeLabels } from "@/utils/iconUtil"; type ObjectMaskEditPaneProps = { polygons?: Polygon[]; @@ -367,6 +368,14 @@ type ZoneObjectSelectorProps = { export function ZoneObjectSelector({ camera }: ZoneObjectSelectorProps) { const { data: config } = useSWR("config"); + const attributeLabels = useMemo(() => { + if (!config) { + return []; + } + + return getAttributeLabels(config); + }, [config]); + const cameraConfig = useMemo(() => { if (config && camera) { return config.cameras[camera]; @@ -382,20 +391,20 @@ export function ZoneObjectSelector({ camera }: ZoneObjectSelectorProps) { Object.values(config.cameras).forEach((camera) => { camera.objects.track.forEach((label) => { - if (!ATTRIBUTE_LABELS.includes(label)) { + if (!attributeLabels.includes(label)) { labels.add(label); } }); }); cameraConfig.objects.track.forEach((label) => { - if (!ATTRIBUTE_LABELS.includes(label)) { + if (!attributeLabels.includes(label)) { labels.add(label); } }); return [...labels].sort(); - }, [config, cameraConfig]); + }, [config, cameraConfig, attributeLabels]); return ( <> diff --git a/web/src/components/settings/ZoneEditPane.tsx b/web/src/components/settings/ZoneEditPane.tsx index 7600c3b29..949cfd1ac 100644 --- a/web/src/components/settings/ZoneEditPane.tsx +++ b/web/src/components/settings/ZoneEditPane.tsx @@ -12,7 +12,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { ATTRIBUTE_LABELS, FrigateConfig } from "@/types/frigateConfig"; +import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; @@ -28,6 +28,7 @@ import { Toaster } from "@/components/ui/sonner"; import { toast } from "sonner"; import { flattenPoints, interpolatePoints } from "@/utils/canvasUtil"; import ActivityIndicator from "../indicators/activity-indicator"; +import { getAttributeLabels } from "@/utils/iconUtil"; type ZoneEditPaneProps = { polygons?: Polygon[]; @@ -505,6 +506,14 @@ export function ZoneObjectSelector({ }: ZoneObjectSelectorProps) { const { data: config } = useSWR("config"); + const attributeLabels = useMemo(() => { + if (!config) { + return []; + } + + return getAttributeLabels(config); + }, [config]); + const cameraConfig = useMemo(() => { if (config && camera) { return config.cameras[camera]; @@ -519,7 +528,7 @@ export function ZoneObjectSelector({ const labels = new Set(); cameraConfig.objects.track.forEach((label) => { - if (!ATTRIBUTE_LABELS.includes(label)) { + if (!attributeLabels.includes(label)) { labels.add(label); } }); @@ -527,7 +536,7 @@ export function ZoneObjectSelector({ if (zoneName) { if (cameraConfig.zones[zoneName]) { cameraConfig.zones[zoneName].objects.forEach((label) => { - if (!ATTRIBUTE_LABELS.includes(label)) { + if (!attributeLabels.includes(label)) { labels.add(label); } }); @@ -535,7 +544,7 @@ export function ZoneObjectSelector({ } return [...labels].sort() || []; - }, [config, cameraConfig, zoneName]); + }, [config, cameraConfig, attributeLabels, zoneName]); const [currentLabels, setCurrentLabels] = useState( selectedLabels, diff --git a/web/src/hooks/use-camera-activity.ts b/web/src/hooks/use-camera-activity.ts index 7b6128ace..bbf70ba32 100644 --- a/web/src/hooks/use-camera-activity.ts +++ b/web/src/hooks/use-camera-activity.ts @@ -3,7 +3,7 @@ import { useInitialCameraState, useMotionActivity, } from "@/api/ws"; -import { ATTRIBUTE_LABELS, CameraConfig } from "@/types/frigateConfig"; +import { CameraConfig, FrigateConfig } from "@/types/frigateConfig"; import { MotionData, ReviewSegment } from "@/types/review"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useTimelineUtils } from "./use-timeline-utils"; @@ -11,6 +11,8 @@ import { ObjectType } from "@/types/ws"; import useDeepMemo from "./use-deep-memo"; import { isEqual } from "lodash"; import { useAutoFrigateStats } from "./use-stats"; +import useSWR from "swr"; +import { getAttributeLabels } from "@/utils/iconUtil"; type useCameraActivityReturn = { activeTracking: boolean; @@ -23,6 +25,16 @@ export function useCameraActivity( camera: CameraConfig, revalidateOnFocus: boolean = true, ): useCameraActivityReturn { + const { data: config } = useSWR("config", { + revalidateOnFocus: false, + }); + const attributeLabels = useMemo(() => { + if (!config) { + return []; + } + + return getAttributeLabels(config); + }, [config]); const [objects, setObjects] = useState([]); // init camera activity @@ -99,7 +111,7 @@ export function useCameraActivity( if (updatedEvent.after.sub_label) { const sub_label = updatedEvent.after.sub_label[0]; - if (ATTRIBUTE_LABELS.includes(sub_label)) { + if (attributeLabels.includes(sub_label)) { label = sub_label; } else { label = `${label}-verified`; @@ -113,7 +125,7 @@ export function useCameraActivity( } handleSetObjects(newObjects); - }, [camera, updatedEvent, objects, handleSetObjects]); + }, [attributeLabels, camera, updatedEvent, objects, handleSetObjects]); // determine if camera is offline diff --git a/web/src/types/frigateConfig.ts b/web/src/types/frigateConfig.ts index 76d9cfa67..1d54b4e7e 100644 --- a/web/src/types/frigateConfig.ts +++ b/web/src/types/frigateConfig.ts @@ -19,14 +19,6 @@ export interface BirdseyeConfig { width: number; } -export const ATTRIBUTE_LABELS = [ - "amazon", - "face", - "fedex", - "license_plate", - "ups", -]; - export type SearchModelSize = "small" | "large"; export interface CameraConfig { diff --git a/web/src/utils/iconUtil.tsx b/web/src/utils/iconUtil.tsx index e3c1f5508..38cea4159 100644 --- a/web/src/utils/iconUtil.tsx +++ b/web/src/utils/iconUtil.tsx @@ -1,4 +1,5 @@ import { IconName } from "@/components/icons/IconPicker"; +import { FrigateConfig } from "@/types/frigateConfig"; import { BsPersonWalking } from "react-icons/bs"; import { FaAmazon, @@ -36,6 +37,19 @@ import { LuBox, LuLassoSelect } from "react-icons/lu"; import * as LuIcons from "react-icons/lu"; import { MdRecordVoiceOver } from "react-icons/md"; +export function getAttributeLabels(config?: FrigateConfig) { + if (!config) { + return []; + } + + const labels = new Set(); + + Object.values(config.model.attributes_map).forEach((values) => + values.forEach((label) => labels.add(label)), + ); + return [...labels]; +} + export function isValidIconName(value: string): value is IconName { return Object.keys(LuIcons).includes(value as IconName); }