import Heading from "@/components/ui/heading"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Event } from "@/types/event"; import { FrigateConfig } from "@/types/frigateConfig"; import { zodResolver } from "@hookform/resolvers/zod"; import axios from "axios"; import { useCallback, useState } from "react"; import { useForm } from "react-hook-form"; import { LuExternalLink } from "react-icons/lu"; import { PiWarningCircle } from "react-icons/pi"; import { Link } from "react-router-dom"; import { toast } from "sonner"; import useSWR from "swr"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { z } from "zod"; import { Button } from "@/components/ui/button"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; type AnnotationSettingsPaneProps = { event: Event; showZones: boolean; setShowZones: React.Dispatch>; annotationOffset: number; setAnnotationOffset: React.Dispatch>; }; export function AnnotationSettingsPane({ event, showZones, setShowZones, annotationOffset, setAnnotationOffset, }: AnnotationSettingsPaneProps) { const { data: config, mutate: updateConfig } = useSWR("config"); const [isLoading, setIsLoading] = useState(false); const formSchema = z.object({ annotationOffset: z.coerce.number().optional().or(z.literal("")), }); const form = useForm>({ resolver: zodResolver(formSchema), mode: "onChange", defaultValues: { annotationOffset: annotationOffset, }, }); const saveToConfig = useCallback( async (annotation_offset: number | string) => { if (!config || !event) { return; } axios .put( `config/set?cameras.${event?.camera}.detect.annotation_offset=${annotation_offset}`, { requires_restart: 0, }, ) .then((res) => { if (res.status === 200) { toast.success( `Annotation offset for ${event?.camera} has been saved to the config file. Restart Frigate to apply your changes.`, { position: "top-center", }, ); updateConfig(); } else { toast.error(`Failed to save config changes: ${res.statusText}`, { position: "top-center", }); } }) .catch((error) => { toast.error( `Failed to save config changes: ${error.response.data.message}`, { position: "top-center" }, ); }) .finally(() => { setIsLoading(false); }); }, [updateConfig, config, event], ); function onSubmit(values: z.infer) { if (!values || values.annotationOffset == null || !config) { return; } setIsLoading(true); saveToConfig(values.annotationOffset); } function onApply(values: z.infer) { if ( !values || values.annotationOffset == null || values.annotationOffset == "" || !config ) { return; } setAnnotationOffset(values.annotationOffset); } return (
Annotation Settings
Always show zones on frames where objects have entered a zone.
( Annotation Offset
This data comes from your camera's detect feed but is overlayed on images from the the record feed. It is unlikely that the two streams are perfectly in sync. As a result, the bounding box and the footage will not line up perfectly. However, the annotation_offset{" "} field in your config can be used to adjust this.
Read the documentation{" "}
Milliseconds to offset detect annotations by.{" "} Default: 0
TIP: Imagine there is an event clip with a person walking from left to right. If the event timeline bounding box is consistently to the left of the person then the value should be decreased. Similarly, if a person is walking from left to right and the bounding box is consistently ahead of the person then the value should be increased.
)} />
); }