2023-12-08 16:33:22 +03:00
|
|
|
|
import {
|
2024-04-19 14:34:07 +03:00
|
|
|
|
DropdownMenu,
|
|
|
|
|
|
DropdownMenuContent,
|
|
|
|
|
|
DropdownMenuLabel,
|
|
|
|
|
|
DropdownMenuSeparator,
|
|
|
|
|
|
DropdownMenuTrigger,
|
|
|
|
|
|
} from "@/components/ui/dropdown-menu";
|
|
|
|
|
|
import {
|
|
|
|
|
|
AlertDialog,
|
|
|
|
|
|
AlertDialogAction,
|
|
|
|
|
|
AlertDialogCancel,
|
|
|
|
|
|
AlertDialogContent,
|
|
|
|
|
|
AlertDialogDescription,
|
|
|
|
|
|
AlertDialogFooter,
|
|
|
|
|
|
AlertDialogHeader,
|
|
|
|
|
|
AlertDialogTitle,
|
|
|
|
|
|
} from "@/components/ui/alert-dialog";
|
|
|
|
|
|
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
2026-02-27 18:55:36 +03:00
|
|
|
|
import {
|
|
|
|
|
|
useCallback,
|
|
|
|
|
|
useEffect,
|
|
|
|
|
|
useMemo,
|
|
|
|
|
|
useState,
|
|
|
|
|
|
type ReactNode,
|
|
|
|
|
|
} from "react";
|
2024-04-19 14:34:07 +03:00
|
|
|
|
import useOptimisticState from "@/hooks/use-optimistic-state";
|
2025-10-08 22:59:21 +03:00
|
|
|
|
import { isMobile } from "react-device-detect";
|
2024-04-19 14:34:07 +03:00
|
|
|
|
import { FaVideo } from "react-icons/fa";
|
|
|
|
|
|
import { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
|
2026-04-27 01:09:35 +03:00
|
|
|
|
import type { ConfigSectionData, JsonObject } from "@/types/configForm";
|
2024-04-19 14:34:07 +03:00
|
|
|
|
import useSWR from "swr";
|
|
|
|
|
|
import FilterSwitch from "@/components/filter/FilterSwitch";
|
|
|
|
|
|
import { ZoneMaskFilterButton } from "@/components/filter/ZoneMaskFilter";
|
|
|
|
|
|
import { PolygonType } from "@/types/canvas";
|
2025-10-13 19:52:08 +03:00
|
|
|
|
import CameraManagementView from "@/views/settings/CameraManagementView";
|
2024-05-29 17:01:39 +03:00
|
|
|
|
import MotionTunerView from "@/views/settings/MotionTunerView";
|
|
|
|
|
|
import MasksAndZonesView from "@/views/settings/MasksAndZonesView";
|
2025-09-12 14:19:29 +03:00
|
|
|
|
import UsersView from "@/views/settings/UsersView";
|
|
|
|
|
|
import RolesView from "@/views/settings/RolesView";
|
2024-10-16 03:25:59 +03:00
|
|
|
|
import UiSettingsView from "@/views/settings/UiSettingsView";
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
import ProfilesView from "@/views/settings/ProfilesView";
|
2025-03-17 21:44:57 +03:00
|
|
|
|
import FrigatePlusSettingsView from "@/views/settings/FrigatePlusSettingsView";
|
2026-03-06 02:19:30 +03:00
|
|
|
|
import MediaSyncSettingsView from "@/views/settings/MediaSyncSettingsView";
|
|
|
|
|
|
import RegionGridSettingsView from "@/views/settings/RegionGridSettingsView";
|
2026-03-19 19:33:42 +03:00
|
|
|
|
import Go2RtcStreamsSettingsView from "@/views/settings/Go2RtcStreamsSettingsView";
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
|
import DetectorsAndModelSettingsView from "@/views/settings/DetectorsAndModelSettingsView";
|
2026-02-27 18:55:36 +03:00
|
|
|
|
import {
|
|
|
|
|
|
SingleSectionPage,
|
|
|
|
|
|
type SettingsPageProps,
|
|
|
|
|
|
type SectionStatus,
|
|
|
|
|
|
} from "@/views/settings/SingleSectionPage";
|
2025-02-10 19:42:35 +03:00
|
|
|
|
import { useSearchEffect } from "@/hooks/use-overlay-state";
|
2025-10-08 22:59:21 +03:00
|
|
|
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
2025-03-03 18:30:52 +03:00
|
|
|
|
import { useInitialCameraState } from "@/api/ws";
|
2025-03-08 19:01:08 +03:00
|
|
|
|
import { useIsAdmin } from "@/hooks/use-is-admin";
|
2025-03-16 18:36:20 +03:00
|
|
|
|
import { useTranslation } from "react-i18next";
|
2026-02-27 18:55:36 +03:00
|
|
|
|
import { useAllCameraOverrides } from "@/hooks/use-config-override";
|
2025-07-07 17:03:57 +03:00
|
|
|
|
import TriggerView from "@/views/settings/TriggerView";
|
2025-11-07 17:02:06 +03:00
|
|
|
|
import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel";
|
2025-10-08 22:59:21 +03:00
|
|
|
|
import {
|
|
|
|
|
|
Sidebar,
|
|
|
|
|
|
SidebarContent,
|
|
|
|
|
|
SidebarGroup,
|
|
|
|
|
|
SidebarGroupLabel,
|
|
|
|
|
|
SidebarInset,
|
|
|
|
|
|
SidebarMenu,
|
|
|
|
|
|
SidebarMenuButton,
|
|
|
|
|
|
SidebarMenuItem,
|
|
|
|
|
|
SidebarMenuSub,
|
|
|
|
|
|
SidebarMenuSubButton,
|
|
|
|
|
|
SidebarMenuSubItem,
|
|
|
|
|
|
SidebarProvider,
|
|
|
|
|
|
} from "@/components/ui/sidebar";
|
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
|
import Heading from "@/components/ui/heading";
|
|
|
|
|
|
import { LuChevronRight } from "react-icons/lu";
|
|
|
|
|
|
import Logo from "@/components/Logo";
|
|
|
|
|
|
import {
|
|
|
|
|
|
MobilePage,
|
|
|
|
|
|
MobilePageContent,
|
|
|
|
|
|
MobilePageHeader,
|
|
|
|
|
|
MobilePageTitle,
|
|
|
|
|
|
} from "@/components/mobile/MobilePage";
|
2026-02-27 18:55:36 +03:00
|
|
|
|
import { Toaster } from "@/components/ui/sonner";
|
|
|
|
|
|
import axios from "axios";
|
|
|
|
|
|
import { toast } from "sonner";
|
|
|
|
|
|
import { mutate } from "swr";
|
|
|
|
|
|
import { RJSFSchema } from "@rjsf/utils";
|
|
|
|
|
|
import {
|
|
|
|
|
|
buildConfigDataForPath,
|
2026-05-19 17:31:50 +03:00
|
|
|
|
buildHiddenFieldContext,
|
2026-04-27 01:09:35 +03:00
|
|
|
|
flattenOverrides,
|
2026-05-18 22:22:54 +03:00
|
|
|
|
getSectionConfig,
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
parseProfileFromSectionPath,
|
2026-02-27 18:55:36 +03:00
|
|
|
|
prepareSectionSavePayload,
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
PROFILE_ELIGIBLE_SECTIONS,
|
2026-05-18 22:22:54 +03:00
|
|
|
|
resolveHiddenFieldEntries,
|
|
|
|
|
|
sanitizeSectionData,
|
2026-02-27 18:55:36 +03:00
|
|
|
|
} from "@/utils/configUtil";
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
import type { ProfileState, ProfilesApiResponse } from "@/types/profile";
|
|
|
|
|
|
import { getProfileColor } from "@/utils/profileColors";
|
2026-05-21 04:55:06 +03:00
|
|
|
|
import { isReplayCamera } from "@/utils/cameraUtil";
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
import { ProfileSectionDropdown } from "@/components/settings/ProfileSectionDropdown";
|
2026-02-27 18:55:36 +03:00
|
|
|
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
|
|
|
|
|
import RestartDialog from "@/components/overlay/dialog/RestartDialog";
|
|
|
|
|
|
import SaveAllPreviewPopover, {
|
|
|
|
|
|
type SaveAllPreviewItem,
|
|
|
|
|
|
} from "@/components/overlay/detail/SaveAllPreviewPopover";
|
|
|
|
|
|
import { useRestart } from "@/api/ws";
|
2026-05-19 06:52:40 +03:00
|
|
|
|
import {
|
|
|
|
|
|
Tooltip,
|
|
|
|
|
|
TooltipContent,
|
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
|
} from "@/components/ui/tooltip";
|
|
|
|
|
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
2024-07-22 23:39:15 +03:00
|
|
|
|
|
|
|
|
|
|
const allSettingsViews = [
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
"uiSettings",
|
|
|
|
|
|
"profiles",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
"globalDetect",
|
|
|
|
|
|
"globalRecording",
|
|
|
|
|
|
"globalSnapshots",
|
|
|
|
|
|
"globalFfmpeg",
|
|
|
|
|
|
"globalMotion",
|
|
|
|
|
|
"globalObjects",
|
|
|
|
|
|
"globalReview",
|
|
|
|
|
|
"globalAudioEvents",
|
|
|
|
|
|
"globalLivePlayback",
|
|
|
|
|
|
"globalTimestampStyle",
|
|
|
|
|
|
"systemDatabase",
|
|
|
|
|
|
"systemTls",
|
|
|
|
|
|
"systemAuthentication",
|
|
|
|
|
|
"systemNetworking",
|
|
|
|
|
|
"systemProxy",
|
|
|
|
|
|
"systemUi",
|
|
|
|
|
|
"systemLogging",
|
|
|
|
|
|
"systemEnvironmentVariables",
|
|
|
|
|
|
"systemTelemetry",
|
|
|
|
|
|
"systemBirdseye",
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
|
"systemDetectorsAndModel",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
"systemMqtt",
|
2026-03-19 19:33:42 +03:00
|
|
|
|
"systemGo2rtcStreams",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
"integrationSemanticSearch",
|
|
|
|
|
|
"integrationGenerativeAi",
|
|
|
|
|
|
"integrationFaceRecognition",
|
|
|
|
|
|
"integrationLpr",
|
|
|
|
|
|
"integrationObjectClassification",
|
|
|
|
|
|
"integrationAudioTranscription",
|
|
|
|
|
|
"cameraDetect",
|
|
|
|
|
|
"cameraFfmpeg",
|
|
|
|
|
|
"cameraRecording",
|
|
|
|
|
|
"cameraSnapshots",
|
|
|
|
|
|
"cameraMotion",
|
|
|
|
|
|
"cameraObjects",
|
2025-10-13 19:52:08 +03:00
|
|
|
|
"cameraReview",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
"cameraAudioEvents",
|
|
|
|
|
|
"cameraAudioTranscription",
|
|
|
|
|
|
"cameraNotifications",
|
|
|
|
|
|
"cameraLivePlayback",
|
|
|
|
|
|
"cameraBirdseye",
|
|
|
|
|
|
"cameraFaceRecognition",
|
|
|
|
|
|
"cameraLpr",
|
|
|
|
|
|
"cameraMqttConfig",
|
|
|
|
|
|
"cameraOnvif",
|
|
|
|
|
|
"cameraTimestampStyle",
|
|
|
|
|
|
"cameraManagement",
|
2025-03-16 18:36:20 +03:00
|
|
|
|
"masksAndZones",
|
|
|
|
|
|
"motionTuner",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
"enrichments",
|
2025-07-07 17:03:57 +03:00
|
|
|
|
"triggers",
|
2024-07-22 23:39:15 +03:00
|
|
|
|
"debug",
|
|
|
|
|
|
"users",
|
2025-09-12 14:19:29 +03:00
|
|
|
|
"roles",
|
2024-07-22 23:39:15 +03:00
|
|
|
|
"notifications",
|
2025-03-17 21:44:57 +03:00
|
|
|
|
"frigateplus",
|
2026-03-06 02:19:30 +03:00
|
|
|
|
"mediaSync",
|
|
|
|
|
|
"regionGrid",
|
2024-07-22 23:39:15 +03:00
|
|
|
|
] as const;
|
|
|
|
|
|
type SettingsType = (typeof allSettingsViews)[number];
|
2024-04-19 14:34:07 +03:00
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
const parsePendingDataKey = (pendingDataKey: string) => {
|
|
|
|
|
|
if (pendingDataKey.includes("::")) {
|
|
|
|
|
|
const idx = pendingDataKey.indexOf("::");
|
|
|
|
|
|
return {
|
|
|
|
|
|
scope: "camera" as const,
|
|
|
|
|
|
cameraName: pendingDataKey.slice(0, idx),
|
|
|
|
|
|
sectionPath: pendingDataKey.slice(idx + 2),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
scope: "global" as const,
|
|
|
|
|
|
cameraName: undefined,
|
|
|
|
|
|
sectionPath: pendingDataKey,
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const createSectionPage = (
|
|
|
|
|
|
sectionKey: string,
|
|
|
|
|
|
level: "global" | "camera",
|
|
|
|
|
|
options?: { showOverrideIndicator?: boolean },
|
|
|
|
|
|
) => {
|
|
|
|
|
|
return (props: SettingsPageProps) => (
|
|
|
|
|
|
<SingleSectionPage
|
|
|
|
|
|
sectionKey={sectionKey}
|
|
|
|
|
|
level={level}
|
|
|
|
|
|
showOverrideIndicator={options?.showOverrideIndicator}
|
|
|
|
|
|
{...props}
|
|
|
|
|
|
/>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const GlobalDetectSettingsPage = createSectionPage("detect", "global");
|
|
|
|
|
|
const GlobalRecordingSettingsPage = createSectionPage("record", "global");
|
|
|
|
|
|
const GlobalSnapshotsSettingsPage = createSectionPage("snapshots", "global");
|
|
|
|
|
|
const GlobalFfmpegSettingsPage = createSectionPage("ffmpeg", "global");
|
|
|
|
|
|
const GlobalMotionSettingsPage = createSectionPage("motion", "global");
|
|
|
|
|
|
const GlobalObjectsSettingsPage = createSectionPage("objects", "global");
|
|
|
|
|
|
const GlobalReviewSettingsPage = createSectionPage("review", "global");
|
|
|
|
|
|
const GlobalAudioEventsSettingsPage = createSectionPage("audio", "global");
|
|
|
|
|
|
const GlobalLivePlaybackSettingsPage = createSectionPage("live", "global");
|
|
|
|
|
|
const GlobalTimestampStyleSettingsPage = createSectionPage(
|
|
|
|
|
|
"timestamp_style",
|
|
|
|
|
|
"global",
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const SystemDatabaseSettingsPage = createSectionPage("database", "global");
|
|
|
|
|
|
const SystemTlsSettingsPage = createSectionPage("tls", "global");
|
|
|
|
|
|
const SystemAuthenticationSettingsPage = createSectionPage("auth", "global");
|
|
|
|
|
|
const SystemNetworkingSettingsPage = createSectionPage("networking", "global");
|
|
|
|
|
|
const SystemProxySettingsPage = createSectionPage("proxy", "global");
|
|
|
|
|
|
const SystemUiSettingsPage = createSectionPage("ui", "global");
|
|
|
|
|
|
const SystemLoggingSettingsPage = createSectionPage("logger", "global");
|
|
|
|
|
|
const SystemEnvironmentVariablesSettingsPage = createSectionPage(
|
|
|
|
|
|
"environment_vars",
|
|
|
|
|
|
"global",
|
|
|
|
|
|
);
|
|
|
|
|
|
const SystemTelemetrySettingsPage = createSectionPage("telemetry", "global");
|
|
|
|
|
|
const SystemBirdseyeSettingsPage = createSectionPage("birdseye", "global");
|
|
|
|
|
|
const NotificationsSettingsPage = createSectionPage("notifications", "global");
|
|
|
|
|
|
|
|
|
|
|
|
const SystemMqttSettingsPage = createSectionPage("mqtt", "global");
|
|
|
|
|
|
const IntegrationSemanticSearchSettingsPage = createSectionPage(
|
|
|
|
|
|
"semantic_search",
|
|
|
|
|
|
"global",
|
|
|
|
|
|
);
|
|
|
|
|
|
const IntegrationGenerativeAiSettingsPage = createSectionPage(
|
|
|
|
|
|
"genai",
|
|
|
|
|
|
"global",
|
|
|
|
|
|
);
|
|
|
|
|
|
const IntegrationFaceRecognitionSettingsPage = createSectionPage(
|
|
|
|
|
|
"face_recognition",
|
|
|
|
|
|
"global",
|
|
|
|
|
|
);
|
|
|
|
|
|
const IntegrationLprSettingsPage = createSectionPage("lpr", "global");
|
|
|
|
|
|
const IntegrationObjectClassificationSettingsPage = createSectionPage(
|
|
|
|
|
|
"classification",
|
|
|
|
|
|
"global",
|
|
|
|
|
|
);
|
|
|
|
|
|
const IntegrationAudioTranscriptionSettingsPage = createSectionPage(
|
|
|
|
|
|
"audio_transcription",
|
|
|
|
|
|
"global",
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const CameraDetectSettingsPage = createSectionPage("detect", "camera");
|
|
|
|
|
|
const CameraFfmpegSettingsPage = createSectionPage("ffmpeg", "camera");
|
|
|
|
|
|
const CameraRecordingSettingsPage = createSectionPage("record", "camera");
|
|
|
|
|
|
const CameraSnapshotsSettingsPage = createSectionPage("snapshots", "camera");
|
|
|
|
|
|
const CameraMotionSettingsPage = createSectionPage("motion", "camera");
|
|
|
|
|
|
const CameraObjectsSettingsPage = createSectionPage("objects", "camera");
|
|
|
|
|
|
const CameraReviewSettingsPage = createSectionPage("review", "camera");
|
|
|
|
|
|
const CameraAudioEventsSettingsPage = createSectionPage("audio", "camera");
|
|
|
|
|
|
const CameraAudioTranscriptionSettingsPage = createSectionPage(
|
|
|
|
|
|
"audio_transcription",
|
|
|
|
|
|
"camera",
|
|
|
|
|
|
);
|
|
|
|
|
|
const CameraNotificationsSettingsPage = createSectionPage(
|
|
|
|
|
|
"notifications",
|
|
|
|
|
|
"camera",
|
|
|
|
|
|
);
|
|
|
|
|
|
const CameraLivePlaybackSettingsPage = createSectionPage("live", "camera");
|
|
|
|
|
|
const CameraBirdseyeSettingsPage = createSectionPage("birdseye", "camera");
|
|
|
|
|
|
const CameraFaceRecognitionSettingsPage = createSectionPage(
|
|
|
|
|
|
"face_recognition",
|
|
|
|
|
|
"camera",
|
|
|
|
|
|
);
|
|
|
|
|
|
const CameraLprSettingsPage = createSectionPage("lpr", "camera");
|
|
|
|
|
|
const CameraMqttConfigSettingsPage = createSectionPage("mqtt", "camera", {
|
|
|
|
|
|
showOverrideIndicator: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
const CameraOnvifSettingsPage = createSectionPage("onvif", "camera", {
|
|
|
|
|
|
showOverrideIndicator: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
const CameraTimestampStyleSettingsPage = createSectionPage(
|
|
|
|
|
|
"timestamp_style",
|
|
|
|
|
|
"camera",
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-10-08 22:59:21 +03:00
|
|
|
|
const settingsGroups = [
|
|
|
|
|
|
{
|
2025-10-09 00:36:23 +03:00
|
|
|
|
label: "general",
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
items: [{ key: "uiSettings", component: UiSettingsView }],
|
2026-02-27 18:55:36 +03:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: "globalConfig",
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{ key: "globalDetect", component: GlobalDetectSettingsPage },
|
|
|
|
|
|
{ key: "globalObjects", component: GlobalObjectsSettingsPage },
|
|
|
|
|
|
{ key: "globalMotion", component: GlobalMotionSettingsPage },
|
|
|
|
|
|
{ key: "globalFfmpeg", component: GlobalFfmpegSettingsPage },
|
|
|
|
|
|
{ key: "globalRecording", component: GlobalRecordingSettingsPage },
|
|
|
|
|
|
{ key: "globalSnapshots", component: GlobalSnapshotsSettingsPage },
|
|
|
|
|
|
{ key: "globalReview", component: GlobalReviewSettingsPage },
|
|
|
|
|
|
{ key: "globalAudioEvents", component: GlobalAudioEventsSettingsPage },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "globalLivePlayback",
|
|
|
|
|
|
component: GlobalLivePlaybackSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "globalTimestampStyle",
|
|
|
|
|
|
component: GlobalTimestampStyleSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
2025-10-08 22:59:21 +03:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-10-09 00:36:23 +03:00
|
|
|
|
label: "cameras",
|
2025-10-08 22:59:21 +03:00
|
|
|
|
items: [
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
{ key: "profiles", component: ProfilesView },
|
2025-10-13 19:52:08 +03:00
|
|
|
|
{ key: "cameraManagement", component: CameraManagementView },
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{ key: "cameraDetect", component: CameraDetectSettingsPage },
|
|
|
|
|
|
{ key: "cameraObjects", component: CameraObjectsSettingsPage },
|
|
|
|
|
|
{ key: "cameraMotion", component: CameraMotionSettingsPage },
|
2025-10-08 22:59:21 +03:00
|
|
|
|
{ key: "motionTuner", component: MotionTunerView },
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{ key: "cameraFfmpeg", component: CameraFfmpegSettingsPage },
|
|
|
|
|
|
{ key: "cameraRecording", component: CameraRecordingSettingsPage },
|
|
|
|
|
|
{ key: "cameraSnapshots", component: CameraSnapshotsSettingsPage },
|
|
|
|
|
|
{ key: "masksAndZones", component: MasksAndZonesView },
|
|
|
|
|
|
{ key: "cameraReview", component: CameraReviewSettingsPage },
|
|
|
|
|
|
{ key: "cameraAudioEvents", component: CameraAudioEventsSettingsPage },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "cameraAudioTranscription",
|
|
|
|
|
|
component: CameraAudioTranscriptionSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ key: "cameraBirdseye", component: CameraBirdseyeSettingsPage },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "cameraLivePlayback",
|
|
|
|
|
|
component: CameraLivePlaybackSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "cameraNotifications",
|
|
|
|
|
|
component: CameraNotificationsSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "cameraFaceRecognition",
|
|
|
|
|
|
component: CameraFaceRecognitionSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ key: "cameraLpr", component: CameraLprSettingsPage },
|
|
|
|
|
|
{ key: "cameraOnvif", component: CameraOnvifSettingsPage },
|
|
|
|
|
|
{ key: "cameraMqttConfig", component: CameraMqttConfigSettingsPage },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "cameraTimestampStyle",
|
|
|
|
|
|
component: CameraTimestampStyleSettingsPage,
|
|
|
|
|
|
},
|
2025-10-08 22:59:21 +03:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-10-09 00:36:23 +03:00
|
|
|
|
label: "enrichments",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
items: [
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "integrationSemanticSearch",
|
|
|
|
|
|
component: IntegrationSemanticSearchSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "integrationGenerativeAi",
|
|
|
|
|
|
component: IntegrationGenerativeAiSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "integrationFaceRecognition",
|
|
|
|
|
|
component: IntegrationFaceRecognitionSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ key: "integrationLpr", component: IntegrationLprSettingsPage },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "integrationObjectClassification",
|
|
|
|
|
|
component: IntegrationObjectClassificationSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ key: "triggers", component: TriggerView },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "integrationAudioTranscription",
|
|
|
|
|
|
component: IntegrationAudioTranscriptionSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: "system",
|
|
|
|
|
|
items: [
|
2026-03-19 19:33:42 +03:00
|
|
|
|
{
|
|
|
|
|
|
key: "systemGo2rtcStreams",
|
|
|
|
|
|
component: Go2RtcStreamsSettingsView,
|
|
|
|
|
|
},
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
|
key: "systemDetectorsAndModel",
|
|
|
|
|
|
component: DetectorsAndModelSettingsView,
|
2026-02-27 18:55:36 +03:00
|
|
|
|
},
|
|
|
|
|
|
{ key: "systemDatabase", component: SystemDatabaseSettingsPage },
|
|
|
|
|
|
{ key: "systemMqtt", component: SystemMqttSettingsPage },
|
|
|
|
|
|
{ key: "systemBirdseye", component: SystemBirdseyeSettingsPage },
|
|
|
|
|
|
{ key: "systemTls", component: SystemTlsSettingsPage },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "systemAuthentication",
|
|
|
|
|
|
component: SystemAuthenticationSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ key: "systemNetworking", component: SystemNetworkingSettingsPage },
|
|
|
|
|
|
{ key: "systemProxy", component: SystemProxySettingsPage },
|
|
|
|
|
|
{ key: "systemUi", component: SystemUiSettingsPage },
|
|
|
|
|
|
{ key: "systemLogging", component: SystemLoggingSettingsPage },
|
|
|
|
|
|
{
|
|
|
|
|
|
key: "systemEnvironmentVariables",
|
|
|
|
|
|
component: SystemEnvironmentVariablesSettingsPage,
|
|
|
|
|
|
},
|
|
|
|
|
|
{ key: "systemTelemetry", component: SystemTelemetrySettingsPage },
|
|
|
|
|
|
],
|
2025-10-08 22:59:21 +03:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-10-09 00:36:23 +03:00
|
|
|
|
label: "users",
|
2025-10-08 22:59:21 +03:00
|
|
|
|
items: [
|
|
|
|
|
|
{ key: "users", component: UsersView },
|
|
|
|
|
|
{ key: "roles", component: RolesView },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-10-09 00:36:23 +03:00
|
|
|
|
label: "notifications",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
items: [{ key: "notifications", component: NotificationsSettingsPage }],
|
2025-10-08 22:59:21 +03:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-10-09 00:36:23 +03:00
|
|
|
|
label: "frigateplus",
|
2025-10-08 22:59:21 +03:00
|
|
|
|
items: [{ key: "frigateplus", component: FrigatePlusSettingsView }],
|
|
|
|
|
|
},
|
2026-01-06 18:20:19 +03:00
|
|
|
|
{
|
|
|
|
|
|
label: "maintenance",
|
2026-03-06 02:19:30 +03:00
|
|
|
|
items: [
|
|
|
|
|
|
{ key: "mediaSync", component: MediaSyncSettingsView },
|
|
|
|
|
|
{ key: "regionGrid", component: RegionGridSettingsView },
|
|
|
|
|
|
],
|
2026-01-06 18:20:19 +03:00
|
|
|
|
},
|
2025-10-08 22:59:21 +03:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-10-13 19:52:08 +03:00
|
|
|
|
const CAMERA_SELECT_BUTTON_PAGES = [
|
|
|
|
|
|
"debug",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
"cameraDetect",
|
|
|
|
|
|
"cameraFfmpeg",
|
|
|
|
|
|
"cameraRecording",
|
|
|
|
|
|
"cameraSnapshots",
|
|
|
|
|
|
"cameraMotion",
|
|
|
|
|
|
"cameraObjects",
|
2025-10-13 19:52:08 +03:00
|
|
|
|
"cameraReview",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
"cameraAudioEvents",
|
|
|
|
|
|
"cameraAudioTranscription",
|
|
|
|
|
|
"cameraNotifications",
|
|
|
|
|
|
"cameraLivePlayback",
|
|
|
|
|
|
"cameraBirdseye",
|
|
|
|
|
|
"cameraFaceRecognition",
|
|
|
|
|
|
"cameraLpr",
|
|
|
|
|
|
"cameraMqttConfig",
|
|
|
|
|
|
"cameraOnvif",
|
|
|
|
|
|
"cameraTimestampStyle",
|
2025-10-13 19:52:08 +03:00
|
|
|
|
"masksAndZones",
|
|
|
|
|
|
"motionTuner",
|
|
|
|
|
|
"triggers",
|
2026-03-06 02:19:30 +03:00
|
|
|
|
"regionGrid",
|
2025-10-13 19:52:08 +03:00
|
|
|
|
];
|
|
|
|
|
|
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const ALLOWED_VIEWS_FOR_VIEWER = ["uiSettings", "notifications"];
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
|
|
|
|
|
// keys for camera sections
|
|
|
|
|
|
const CAMERA_SECTION_MAPPING: Record<string, SettingsType> = {
|
|
|
|
|
|
detect: "cameraDetect",
|
|
|
|
|
|
ffmpeg: "cameraFfmpeg",
|
|
|
|
|
|
record: "cameraRecording",
|
|
|
|
|
|
snapshots: "cameraSnapshots",
|
|
|
|
|
|
motion: "cameraMotion",
|
|
|
|
|
|
objects: "cameraObjects",
|
|
|
|
|
|
review: "cameraReview",
|
|
|
|
|
|
audio: "cameraAudioEvents",
|
|
|
|
|
|
audio_transcription: "cameraAudioTranscription",
|
|
|
|
|
|
notifications: "cameraNotifications",
|
|
|
|
|
|
live: "cameraLivePlayback",
|
|
|
|
|
|
birdseye: "cameraBirdseye",
|
|
|
|
|
|
face_recognition: "cameraFaceRecognition",
|
|
|
|
|
|
lpr: "cameraLpr",
|
|
|
|
|
|
mqtt: "cameraMqttConfig",
|
|
|
|
|
|
onvif: "cameraOnvif",
|
|
|
|
|
|
timestamp_style: "cameraTimestampStyle",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
// Reverse mapping: page key → config section key
|
|
|
|
|
|
const REVERSE_CAMERA_SECTION_MAPPING: Record<string, string> =
|
|
|
|
|
|
Object.fromEntries(
|
|
|
|
|
|
Object.entries(CAMERA_SECTION_MAPPING).map(([section, page]) => [
|
|
|
|
|
|
page,
|
|
|
|
|
|
section,
|
|
|
|
|
|
]),
|
|
|
|
|
|
);
|
|
|
|
|
|
// masksAndZones is a composite page, not in CAMERA_SECTION_MAPPING
|
|
|
|
|
|
REVERSE_CAMERA_SECTION_MAPPING["masksAndZones"] = "masksAndZones";
|
|
|
|
|
|
|
|
|
|
|
|
// Pages where the profile dropdown should appear
|
|
|
|
|
|
const PROFILE_DROPDOWN_PAGES = new Set(
|
|
|
|
|
|
Object.entries(REVERSE_CAMERA_SECTION_MAPPING)
|
|
|
|
|
|
.filter(
|
|
|
|
|
|
([, sectionKey]) =>
|
|
|
|
|
|
PROFILE_ELIGIBLE_SECTIONS.has(sectionKey) ||
|
|
|
|
|
|
sectionKey === "masksAndZones",
|
|
|
|
|
|
)
|
|
|
|
|
|
.map(([pageKey]) => pageKey),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
// keys for global sections
|
|
|
|
|
|
const GLOBAL_SECTION_MAPPING: Record<string, SettingsType> = {
|
|
|
|
|
|
detect: "globalDetect",
|
|
|
|
|
|
ffmpeg: "globalFfmpeg",
|
|
|
|
|
|
record: "globalRecording",
|
|
|
|
|
|
snapshots: "globalSnapshots",
|
|
|
|
|
|
motion: "globalMotion",
|
|
|
|
|
|
objects: "globalObjects",
|
|
|
|
|
|
review: "globalReview",
|
|
|
|
|
|
audio: "globalAudioEvents",
|
|
|
|
|
|
live: "globalLivePlayback",
|
|
|
|
|
|
timestamp_style: "globalTimestampStyle",
|
|
|
|
|
|
notifications: "notifications",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const ENRICHMENTS_SECTION_MAPPING: Record<string, SettingsType> = {
|
|
|
|
|
|
semantic_search: "integrationSemanticSearch",
|
|
|
|
|
|
genai: "integrationGenerativeAi",
|
|
|
|
|
|
face_recognition: "integrationFaceRecognition",
|
|
|
|
|
|
lpr: "integrationLpr",
|
|
|
|
|
|
classification: "integrationObjectClassification",
|
|
|
|
|
|
audio_transcription: "integrationAudioTranscription",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const SYSTEM_SECTION_MAPPING: Record<string, SettingsType> = {
|
2026-03-19 19:33:42 +03:00
|
|
|
|
go2rtc_streams: "systemGo2rtcStreams",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
database: "systemDatabase",
|
|
|
|
|
|
mqtt: "systemMqtt",
|
|
|
|
|
|
tls: "systemTls",
|
|
|
|
|
|
auth: "systemAuthentication",
|
|
|
|
|
|
networking: "systemNetworking",
|
|
|
|
|
|
proxy: "systemProxy",
|
|
|
|
|
|
ui: "systemUi",
|
|
|
|
|
|
logger: "systemLogging",
|
|
|
|
|
|
environment_vars: "systemEnvironmentVariables",
|
|
|
|
|
|
telemetry: "systemTelemetry",
|
|
|
|
|
|
birdseye: "systemBirdseye",
|
Merge detector and model in settings UI (#23216)
* add embedded mode to BaseSection so parents can host the save action
* add optional action slot to current Frigate+ model summary
* add w-full to action slot flex wrapper for explicit width contract
* i18n
* merged detectors and model settings view
* fix document title
* Embed detector form in merged settings view
* add detection model card with tabs and custom model embed
* add Frigate+ model selector with filter popover to merged page
* Add mismatch banner and gate save on detector and model compatibility
* Wire atomic save, restart toast, and undo on detectors and model page
* Clear child pending data on undo
* route merged detectors and model view in settings
* trim Frigate+ page to account-only and remove old detection model view
* basic e2e
* Fix unsaved-changes guard, custom path leak, and post-failure cache resync
* Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
* Hide Plus/Custom tabs when Frigate+ is not enabled
* Detect active Plus model via model.plus.id instead of path prefix
* Sync state back to snapshot when child form un-modifies and remount on undo
* Always require restart on save since model changes also need one
* Wrap Frigate+ model selector in SplitCardRow with label and description
* rename tab
* update docs
* sync top-level model with default detector's resolved model
when the user doesn't define a top-level `model:` block, `FrigateConfig.model` stayed at pydantic field defaults (320×320, /labelmap.txt) while the per-detector model picked up `DEFAULT_MODEL` for openvino on cpu (300×300, coco_91cl_bkgr.txt introduced in #23127), causing `RemoteObjectDetector` to fail with "buffer is too small for requested array" because the SHM was sized from the per-detector model but mapped using the top-level one. After the detector loop, copy the first detector's resolved model up to `self.model` so both sides agree on dimensions and labelmap
* revert to cpu detector by default
use openvino cpu for new configs only
* add defaults
2026-05-17 20:54:21 +03:00
|
|
|
|
detectors: "systemDetectorsAndModel",
|
|
|
|
|
|
model: "systemDetectorsAndModel",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const CAMERA_SECTION_KEYS = new Set<SettingsType>(
|
|
|
|
|
|
Object.values(CAMERA_SECTION_MAPPING),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-10-08 22:59:21 +03:00
|
|
|
|
const getCurrentComponent = (page: SettingsType) => {
|
|
|
|
|
|
for (const group of settingsGroups) {
|
|
|
|
|
|
for (const item of group.items) {
|
|
|
|
|
|
if (item.key === page) {
|
|
|
|
|
|
return item.component;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function MobileMenuItem({
|
|
|
|
|
|
item,
|
|
|
|
|
|
onSelect,
|
|
|
|
|
|
onClose,
|
|
|
|
|
|
className,
|
2026-02-27 18:55:36 +03:00
|
|
|
|
label,
|
2025-10-08 22:59:21 +03:00
|
|
|
|
}: {
|
|
|
|
|
|
item: { key: string };
|
|
|
|
|
|
onSelect: (key: string) => void;
|
|
|
|
|
|
onClose?: () => void;
|
|
|
|
|
|
className?: string;
|
2026-02-27 18:55:36 +03:00
|
|
|
|
label?: ReactNode;
|
2025-10-08 22:59:21 +03:00
|
|
|
|
}) {
|
|
|
|
|
|
const { t } = useTranslation(["views/settings"]);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2025-11-05 17:49:31 +03:00
|
|
|
|
<div
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"inline-flex h-10 w-full cursor-pointer items-center justify-between whitespace-nowrap rounded-md px-4 py-2 pr-2 text-sm font-medium text-primary-variant disabled:pointer-events-none disabled:opacity-50",
|
|
|
|
|
|
className,
|
|
|
|
|
|
)}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
onClick={() => {
|
|
|
|
|
|
onSelect(item.key);
|
|
|
|
|
|
onClose?.();
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div className="w-full">
|
|
|
|
|
|
{label ?? <div>{t("menu." + item.key)}</div>}
|
|
|
|
|
|
</div>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
<LuChevronRight className="size-4" />
|
2025-11-05 17:49:31 +03:00
|
|
|
|
</div>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
export default function Settings() {
|
2025-03-16 18:36:20 +03:00
|
|
|
|
const { t } = useTranslation(["views/settings"]);
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const [page, setPage] = useState<SettingsType>("uiSettings");
|
2024-04-19 14:34:07 +03:00
|
|
|
|
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
|
2025-10-08 22:59:21 +03:00
|
|
|
|
const [contentMobileOpen, setContentMobileOpen] = useState(false);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
const [sectionStatusByKey, setSectionStatusByKey] = useState<
|
|
|
|
|
|
Partial<Record<SettingsType, SectionStatus>>
|
|
|
|
|
|
>({});
|
2024-04-19 14:34:07 +03:00
|
|
|
|
|
|
|
|
|
|
const { data: config } = useSWR<FrigateConfig>("config");
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const { data: profilesData } = useSWR<ProfilesApiResponse>("profiles");
|
2024-04-19 14:34:07 +03:00
|
|
|
|
|
2025-02-15 16:56:45 +03:00
|
|
|
|
const [searchParams] = useSearchParams();
|
|
|
|
|
|
|
2025-03-08 19:01:08 +03:00
|
|
|
|
// auth and roles
|
|
|
|
|
|
|
|
|
|
|
|
const isAdmin = useIsAdmin();
|
|
|
|
|
|
|
|
|
|
|
|
const visibleSettingsViews = !isAdmin
|
2025-10-13 19:52:08 +03:00
|
|
|
|
? ALLOWED_VIEWS_FOR_VIEWER
|
2025-03-08 19:01:08 +03:00
|
|
|
|
: allSettingsViews;
|
|
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
const [unsavedChanges, setUnsavedChanges] = useState(false);
|
|
|
|
|
|
const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
// Store pending form data keyed by "sectionKey" or "cameraName::sectionKey"
|
|
|
|
|
|
const [pendingDataBySection, setPendingDataBySection] = useState<
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
Record<string, ConfigSectionData>
|
|
|
|
|
|
>({});
|
|
|
|
|
|
|
|
|
|
|
|
// Profile editing state
|
|
|
|
|
|
const [editingProfile, setEditingProfile] = useState<
|
|
|
|
|
|
Record<string, string | null>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
>({});
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const [profilesUIEnabled, setProfilesUIEnabled] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
const allProfileNames = useMemo(() => {
|
|
|
|
|
|
if (!config?.profiles) return [];
|
|
|
|
|
|
return Object.keys(config.profiles).sort();
|
|
|
|
|
|
}, [config]);
|
|
|
|
|
|
|
|
|
|
|
|
const profileFriendlyNames = useMemo(() => {
|
|
|
|
|
|
const map = new Map<string, string>();
|
|
|
|
|
|
if (profilesData?.profiles) {
|
|
|
|
|
|
profilesData.profiles.forEach((p) => map.set(p.name, p.friendly_name));
|
|
|
|
|
|
}
|
|
|
|
|
|
return map;
|
|
|
|
|
|
}, [profilesData]);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
2025-10-08 22:59:21 +03:00
|
|
|
|
const navigate = useNavigate();
|
|
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
const cameras = useMemo(() => {
|
|
|
|
|
|
if (!config) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Object.values(config.cameras)
|
2026-05-21 04:55:06 +03:00
|
|
|
|
.filter(
|
|
|
|
|
|
(conf) =>
|
|
|
|
|
|
conf.ui.dashboard &&
|
|
|
|
|
|
conf.enabled_in_config &&
|
|
|
|
|
|
!isReplayCamera(conf.name),
|
|
|
|
|
|
)
|
2024-04-19 14:34:07 +03:00
|
|
|
|
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
|
|
|
|
|
}, [config]);
|
|
|
|
|
|
|
|
|
|
|
|
const [selectedCamera, setSelectedCamera] = useState<string>("");
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
// Get all camera overrides for the selected camera
|
|
|
|
|
|
const cameraOverrides = useAllCameraOverrides(config, selectedCamera);
|
|
|
|
|
|
|
2025-03-03 18:30:52 +03:00
|
|
|
|
const { payload: allCameraStates } = useInitialCameraState(
|
|
|
|
|
|
cameras.length > 0 ? cameras[0].name : "",
|
|
|
|
|
|
true,
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const cameraEnabledStates = useMemo(() => {
|
|
|
|
|
|
const states: Record<string, boolean> = {};
|
|
|
|
|
|
if (allCameraStates) {
|
|
|
|
|
|
Object.entries(allCameraStates).forEach(([camName, state]) => {
|
|
|
|
|
|
states[camName] = state.config?.enabled ?? false;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
// fallback to config if ws data isn’t available yet
|
|
|
|
|
|
cameras.forEach((cam) => {
|
|
|
|
|
|
if (!(cam.name in states)) {
|
|
|
|
|
|
states[cam.name] = cam.enabled;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return states;
|
|
|
|
|
|
}, [allCameraStates, cameras]);
|
|
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
const [filterZoneMask, setFilterZoneMask] = useState<PolygonType[]>();
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
// Save All state
|
|
|
|
|
|
const [isSavingAll, setIsSavingAll] = useState(false);
|
2026-03-26 21:47:24 +03:00
|
|
|
|
const [isAnySectionSaving, setIsAnySectionSaving] = useState(false);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
const [restartDialogOpen, setRestartDialogOpen] = useState(false);
|
|
|
|
|
|
const { send: sendRestart } = useRestart();
|
|
|
|
|
|
const { data: fullSchema } = useSWR<RJSFSchema>("config/schema.json");
|
|
|
|
|
|
|
|
|
|
|
|
const hasPendingChanges = Object.keys(pendingDataBySection).length > 0;
|
|
|
|
|
|
const hasPendingValidationErrors = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
Object.values(sectionStatusByKey).some(
|
|
|
|
|
|
(status) => !!status && status.hasChanges && status.hasValidationErrors,
|
|
|
|
|
|
),
|
|
|
|
|
|
[sectionStatusByKey],
|
|
|
|
|
|
);
|
|
|
|
|
|
const pendingChangesPreview = useMemo<SaveAllPreviewItem[]>(() => {
|
|
|
|
|
|
if (!config || !fullSchema) return [];
|
|
|
|
|
|
|
|
|
|
|
|
const items: SaveAllPreviewItem[] = [];
|
|
|
|
|
|
Object.entries(pendingDataBySection).forEach(
|
|
|
|
|
|
([pendingDataKey, pendingData]) => {
|
|
|
|
|
|
const payload = prepareSectionSavePayload({
|
|
|
|
|
|
pendingDataKey,
|
|
|
|
|
|
pendingData,
|
|
|
|
|
|
config,
|
|
|
|
|
|
fullSchema,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!payload) return;
|
|
|
|
|
|
|
|
|
|
|
|
const { scope, cameraName, sectionPath } =
|
|
|
|
|
|
parsePendingDataKey(pendingDataKey);
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const { isProfile, profileName, actualSection } =
|
|
|
|
|
|
parseProfileFromSectionPath(sectionPath);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
const flattened = flattenOverrides(payload.sanitizedOverrides);
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const displaySection = isProfile ? actualSection : sectionPath;
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
|
|
|
|
|
flattened.forEach(({ path, value }) => {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const fieldPath = path ? `${displaySection}.${path}` : displaySection;
|
|
|
|
|
|
items.push({
|
|
|
|
|
|
scope,
|
|
|
|
|
|
cameraName,
|
|
|
|
|
|
profileName: isProfile
|
|
|
|
|
|
? (profileFriendlyNames.get(profileName!) ?? profileName)
|
|
|
|
|
|
: undefined,
|
|
|
|
|
|
fieldPath,
|
|
|
|
|
|
value,
|
|
|
|
|
|
});
|
2026-02-27 18:55:36 +03:00
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
return items.sort((left, right) => {
|
|
|
|
|
|
const scopeCompare = left.scope.localeCompare(right.scope);
|
|
|
|
|
|
if (scopeCompare !== 0) return scopeCompare;
|
|
|
|
|
|
const cameraCompare = (left.cameraName ?? "").localeCompare(
|
|
|
|
|
|
right.cameraName ?? "",
|
|
|
|
|
|
);
|
|
|
|
|
|
if (cameraCompare !== 0) return cameraCompare;
|
|
|
|
|
|
return left.fieldPath.localeCompare(right.fieldPath);
|
|
|
|
|
|
});
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
}, [config, fullSchema, pendingDataBySection, profileFriendlyNames]);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
|
|
|
|
|
// Map a pendingDataKey to SettingsType menu key for clearing section status
|
|
|
|
|
|
const pendingKeyToMenuKey = useCallback(
|
|
|
|
|
|
(pendingDataKey: string): SettingsType | undefined => {
|
|
|
|
|
|
let sectionPath: string;
|
|
|
|
|
|
let level: "global" | "camera";
|
|
|
|
|
|
|
|
|
|
|
|
if (pendingDataKey.includes("::")) {
|
|
|
|
|
|
sectionPath = pendingDataKey.slice(pendingDataKey.indexOf("::") + 2);
|
|
|
|
|
|
level = "camera";
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sectionPath = pendingDataKey;
|
|
|
|
|
|
level = "global";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
// For profile keys like "profiles.armed.detect", extract the actual section
|
|
|
|
|
|
const { actualSection } = parseProfileFromSectionPath(sectionPath);
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
if (level === "camera") {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
return CAMERA_SECTION_MAPPING[actualSection] as
|
|
|
|
|
|
| SettingsType
|
|
|
|
|
|
| undefined;
|
2026-02-27 18:55:36 +03:00
|
|
|
|
}
|
|
|
|
|
|
return (
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
(GLOBAL_SECTION_MAPPING[actualSection] as SettingsType | undefined) ??
|
|
|
|
|
|
(ENRICHMENTS_SECTION_MAPPING[actualSection] as
|
2026-02-27 18:55:36 +03:00
|
|
|
|
| SettingsType
|
|
|
|
|
|
| undefined) ??
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
(SYSTEM_SECTION_MAPPING[actualSection] as SettingsType | undefined)
|
2026-02-27 18:55:36 +03:00
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
[],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-05-18 22:22:54 +03:00
|
|
|
|
// Show save/undo all buttons only when at least one pending change lives
|
|
|
|
|
|
// outside the currently visible page. Map each pending key to its menu key
|
|
|
|
|
|
// (e.g. both `detectors` and `model` collapse to `systemDetectorsAndModel`)
|
|
|
|
|
|
// so a composite page with two pending config-sections still counts as one.
|
2026-04-07 16:16:19 +03:00
|
|
|
|
const showSaveAllButtons = useMemo(() => {
|
|
|
|
|
|
const pendingKeys = Object.keys(pendingDataBySection);
|
|
|
|
|
|
if (pendingKeys.length === 0) return false;
|
|
|
|
|
|
|
2026-05-18 22:22:54 +03:00
|
|
|
|
for (const key of pendingKeys) {
|
|
|
|
|
|
const menuKey = pendingKeyToMenuKey(key);
|
|
|
|
|
|
if (menuKey !== pageToggle) return true;
|
|
|
|
|
|
if (key.includes("::")) {
|
|
|
|
|
|
const cameraName = key.slice(0, key.indexOf("::"));
|
|
|
|
|
|
if (cameraName !== selectedCamera) return true;
|
|
|
|
|
|
}
|
2026-04-07 16:16:19 +03:00
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}, [pendingDataBySection, pendingKeyToMenuKey, pageToggle, selectedCamera]);
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
const handleSaveAll = useCallback(async () => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!config ||
|
|
|
|
|
|
!fullSchema ||
|
|
|
|
|
|
!hasPendingChanges ||
|
|
|
|
|
|
hasPendingValidationErrors
|
|
|
|
|
|
)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
setIsSavingAll(true);
|
|
|
|
|
|
let successCount = 0;
|
|
|
|
|
|
let failCount = 0;
|
|
|
|
|
|
let anyNeedsRestart = false;
|
|
|
|
|
|
const savedKeys: string[] = [];
|
2026-05-18 22:22:54 +03:00
|
|
|
|
// Pending entries that have been successfully PUT — cleared in one batch
|
|
|
|
|
|
// after `mutate("config")` resolves
|
|
|
|
|
|
const keysToClear: string[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
// `detectors` and `model` are owned by DetectorsAndModelSettingsView,
|
|
|
|
|
|
// which saves them atomically (single combined PUT with a pre-clear when
|
|
|
|
|
|
// detector keys change or the Plus/Custom tab flips). Doing the same here
|
|
|
|
|
|
// keeps Save All consistent with the page's own Save button
|
|
|
|
|
|
const hasPendingDetectors = "detectors" in pendingDataBySection;
|
|
|
|
|
|
const hasPendingModel = "model" in pendingDataBySection;
|
|
|
|
|
|
if (hasPendingDetectors || hasPendingModel) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const pendingDetectors = hasPendingDetectors
|
|
|
|
|
|
? pendingDataBySection.detectors
|
|
|
|
|
|
: undefined;
|
|
|
|
|
|
const pendingModel = hasPendingModel
|
|
|
|
|
|
? pendingDataBySection.model
|
|
|
|
|
|
: undefined;
|
|
|
|
|
|
|
|
|
|
|
|
// Hidden-field lists come from the section configs themselves so
|
|
|
|
|
|
// they stay in sync with what the embedded forms strip on render
|
|
|
|
|
|
const detectorHiddenFields = resolveHiddenFieldEntries(
|
|
|
|
|
|
getSectionConfig("detectors", "global").hiddenFields,
|
2026-05-19 17:31:50 +03:00
|
|
|
|
buildHiddenFieldContext(config, "global"),
|
2026-05-18 22:22:54 +03:00
|
|
|
|
);
|
|
|
|
|
|
const modelHiddenFields = resolveHiddenFieldEntries(
|
|
|
|
|
|
getSectionConfig("model", "global").hiddenFields,
|
2026-05-19 17:31:50 +03:00
|
|
|
|
buildHiddenFieldContext(config, "global"),
|
2026-05-18 22:22:54 +03:00
|
|
|
|
);
|
|
|
|
|
|
const sanitizedDetectors =
|
|
|
|
|
|
pendingDetectors !== undefined
|
|
|
|
|
|
? sanitizeSectionData(pendingDetectors, detectorHiddenFields)
|
|
|
|
|
|
: undefined;
|
|
|
|
|
|
const sanitizedModel =
|
|
|
|
|
|
pendingModel !== undefined
|
|
|
|
|
|
? sanitizeSectionData(pendingModel, modelHiddenFields)
|
|
|
|
|
|
: undefined;
|
|
|
|
|
|
|
|
|
|
|
|
// Pre-clear conditions: detector keys differ from saved config (rename
|
|
|
|
|
|
// or add/remove), OR the model save flips between Plus and Custom modes
|
|
|
|
|
|
let detectorKeysChanged = false;
|
|
|
|
|
|
if (sanitizedDetectors && typeof sanitizedDetectors === "object") {
|
|
|
|
|
|
const pendingKeySet = Object.keys(
|
|
|
|
|
|
sanitizedDetectors as JsonObject,
|
|
|
|
|
|
).sort();
|
|
|
|
|
|
const savedKeySet = Object.keys(config.detectors ?? {}).sort();
|
|
|
|
|
|
detectorKeysChanged =
|
|
|
|
|
|
JSON.stringify(pendingKeySet) !== JSON.stringify(savedKeySet);
|
|
|
|
|
|
}
|
|
|
|
|
|
let modelTabChanged = false;
|
|
|
|
|
|
if (sanitizedModel && typeof sanitizedModel === "object") {
|
|
|
|
|
|
const newPath = (sanitizedModel as { path?: string }).path;
|
|
|
|
|
|
const oldPath = config.model?.path;
|
|
|
|
|
|
const newIsPlus =
|
|
|
|
|
|
typeof newPath === "string" && newPath.startsWith("plus://");
|
|
|
|
|
|
const oldIsPlus =
|
|
|
|
|
|
typeof oldPath === "string" && oldPath.startsWith("plus://");
|
|
|
|
|
|
modelTabChanged = newIsPlus !== oldIsPlus;
|
|
|
|
|
|
}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
2026-05-18 22:22:54 +03:00
|
|
|
|
if (detectorKeysChanged || modelTabChanged) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await axios.put("config/set", {
|
|
|
|
|
|
requires_restart: 0,
|
|
|
|
|
|
config_data: { detectors: null, model: null },
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// best-effort cleanup; the merge-write below will surface any
|
|
|
|
|
|
// real error.
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const combinedConfigData: Record<string, unknown> = {};
|
|
|
|
|
|
if (sanitizedDetectors !== undefined) {
|
|
|
|
|
|
combinedConfigData.detectors = sanitizedDetectors;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sanitizedModel !== undefined) {
|
|
|
|
|
|
combinedConfigData.model = sanitizedModel;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await axios.put("config/set", {
|
|
|
|
|
|
requires_restart: 0,
|
|
|
|
|
|
config_data: combinedConfigData,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (hasPendingDetectors) {
|
|
|
|
|
|
keysToClear.push("detectors");
|
|
|
|
|
|
savedKeys.push("detectors");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (hasPendingModel) {
|
|
|
|
|
|
keysToClear.push("model");
|
|
|
|
|
|
savedKeys.push("model");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (hasPendingDetectors || hasPendingModel) {
|
|
|
|
|
|
successCount++;
|
|
|
|
|
|
anyNeedsRestart = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
|
console.error(
|
|
|
|
|
|
"Save All – error saving detectors/model atomically",
|
|
|
|
|
|
error,
|
|
|
|
|
|
);
|
|
|
|
|
|
if (hasPendingDetectors || hasPendingModel) {
|
|
|
|
|
|
failCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const pendingKeys = Object.keys(pendingDataBySection).filter(
|
|
|
|
|
|
(key) => key !== "detectors" && key !== "model",
|
|
|
|
|
|
);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
|
|
|
|
|
for (const key of pendingKeys) {
|
|
|
|
|
|
const pendingData = pendingDataBySection[key];
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
try {
|
|
|
|
|
|
const payload = prepareSectionSavePayload({
|
|
|
|
|
|
pendingDataKey: key,
|
|
|
|
|
|
pendingData,
|
|
|
|
|
|
config,
|
|
|
|
|
|
fullSchema,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!payload) {
|
2026-05-18 22:22:54 +03:00
|
|
|
|
// No actual overrides — schedule the pending entry for clearing
|
|
|
|
|
|
keysToClear.push(key);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
successCount++;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const configData = buildConfigDataForPath(
|
|
|
|
|
|
payload.basePath,
|
|
|
|
|
|
payload.sanitizedOverrides,
|
|
|
|
|
|
);
|
|
|
|
|
|
await axios.put("config/set", {
|
|
|
|
|
|
requires_restart: payload.needsRestart ? 1 : 0,
|
|
|
|
|
|
update_topic: payload.updateTopic,
|
|
|
|
|
|
config_data: configData,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (payload.needsRestart) {
|
|
|
|
|
|
anyNeedsRestart = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 22:22:54 +03:00
|
|
|
|
// Defer clearing the pending entry until after mutate("config") resolves
|
|
|
|
|
|
keysToClear.push(key);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
savedKeys.push(key);
|
|
|
|
|
|
successCount++;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
|
console.error("Save All – error saving", key, error);
|
|
|
|
|
|
failCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-18 22:22:54 +03:00
|
|
|
|
// Refresh config from server once — must complete before clearing the
|
|
|
|
|
|
// pending entries so consumers don't observe a moment where pending is
|
|
|
|
|
|
// empty AND config is still stale
|
2026-02-27 18:55:36 +03:00
|
|
|
|
await mutate("config");
|
UI fixes (#23127)
* hide camera overrides badge from system sections
* show empty card on camera metrics page when no cameras are defined
* fix enabled camera state switch after adding via wizard
Cameras added mid-session have no WS state until the dispatcher publishes camera_activity (which only happens on a fresh onConnect). Fall back to the config's enabled value so the switch reflects reality immediately after the wizard closes.
* guard camera enabled access
console would throw errors after adding via camera wizard
* fix useOptimisticState dropping debounced setState under StrictMode
* use openvino on cpu as default model
- faster than tflite on cpu
- add to default generated config
* use an enum for model_size
the frontend will then render this as a select dropdown because of the changes in the json schema
* i18n
* sync object filter entries with tracked labels in camera config form
Filter sub-collapsibles in the camera Objects section are driven by `filters` dict keys, but profile merges and live track-switch edits don't add matching entries, so newly tracked labels (like from a profile override) had no collapsible. Synthesize default filter entries from `track` in the form data so every tracked label renders a collapsible; baseline data also gets the synthesized entries, so save payloads are unchanged.
* revalidate raw paths cache after config save so CameraPathWidget shows fresh credentials
* fix test
* restore masked ffmpeg credentials when persisting camera config
* formatting
* rebuild ffmpeg commands when enabling recording for the first time
Toggling record.enabled from the config UI updated the in-memory config but left ffmpeg running with its original command, so the record output args were never wired in and nothing landed in the cache for the maintainer to move. The record config update now rebuilds ffmpeg_cmds when enabled_in_config transitions, and the camera watchdog restarts ffmpeg on a false to true transition so the record output gets wired in. MQTT toggles, which only flip record.enabled at runtime, are unaffected and continue to work via the maintainer's drop/keep gate.
* keep record toggle switch in single camera view disabled until enabled in config
* fix override detection for sections unset in the global config
Override badges and the blue dot now compare against schema defaults for sections like motion that the API serializes as null when omitted from the global YAML, instead of treating any populated camera config as an override
* add support for config-aware patterns in section hiddenFields
Section configs can now declare dynamic hidden-field entries as functions of the loaded config; objects.ts uses this to hide auto-populated attribute filters (DHL, face, license_plate, etc.) from the form, save flow, and override popover when those labels aren't user-settable
* siimplify object filters handling
live updating was getting very messy. users will just need to save once they enable a new object in order to see filters for that object
* tweaks
* update docs for new detector default
* make genai provider required and add special case for UI
prevent validation errors from appearing on initial creation of genai provider by setting the first option in the select dropdown as default
2026-05-07 16:53:07 +03:00
|
|
|
|
mutate("config/raw_paths");
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
2026-05-18 22:22:54 +03:00
|
|
|
|
if (keysToClear.length > 0) {
|
|
|
|
|
|
setPendingDataBySection((prev) => {
|
|
|
|
|
|
const next = { ...prev };
|
|
|
|
|
|
for (const key of keysToClear) {
|
|
|
|
|
|
delete next[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
return next;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
// Clear hasChanges in sidebar for all successfully saved sections
|
|
|
|
|
|
if (savedKeys.length > 0) {
|
|
|
|
|
|
setSectionStatusByKey((prev) => {
|
|
|
|
|
|
const updated = { ...prev };
|
|
|
|
|
|
for (const key of savedKeys) {
|
|
|
|
|
|
const menuKey = pendingKeyToMenuKey(key);
|
|
|
|
|
|
if (menuKey && updated[menuKey]) {
|
|
|
|
|
|
updated[menuKey] = {
|
|
|
|
|
|
...updated[menuKey],
|
|
|
|
|
|
hasChanges: false,
|
|
|
|
|
|
hasValidationErrors: false,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return updated;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Aggregate toast
|
|
|
|
|
|
const totalCount = successCount + failCount;
|
|
|
|
|
|
if (failCount === 0) {
|
|
|
|
|
|
if (anyNeedsRestart) {
|
|
|
|
|
|
toast.success(
|
2026-05-18 22:22:54 +03:00
|
|
|
|
t("toast.saveAllSuccessRestartRequired", {
|
2026-02-27 18:55:36 +03:00
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
count: successCount,
|
|
|
|
|
|
}),
|
|
|
|
|
|
{
|
2026-05-18 22:22:54 +03:00
|
|
|
|
duration: 10000,
|
2026-02-27 18:55:36 +03:00
|
|
|
|
action: (
|
|
|
|
|
|
<a onClick={() => setRestartDialogOpen(true)}>
|
|
|
|
|
|
<Button>
|
|
|
|
|
|
{t("restart.button", { ns: "components/dialog" })}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
toast.success(
|
|
|
|
|
|
t("toast.saveAllSuccess", {
|
|
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
count: successCount,
|
|
|
|
|
|
}),
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (successCount > 0) {
|
|
|
|
|
|
toast.warning(
|
|
|
|
|
|
t("toast.saveAllPartial", {
|
|
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
count: totalCount,
|
|
|
|
|
|
successCount,
|
|
|
|
|
|
totalCount,
|
|
|
|
|
|
failCount,
|
|
|
|
|
|
}),
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
toast.error(t("toast.saveAllFailure", { ns: "views/settings" }));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setIsSavingAll(false);
|
|
|
|
|
|
}, [
|
|
|
|
|
|
config,
|
|
|
|
|
|
fullSchema,
|
|
|
|
|
|
hasPendingChanges,
|
|
|
|
|
|
hasPendingValidationErrors,
|
|
|
|
|
|
pendingDataBySection,
|
|
|
|
|
|
pendingKeyToMenuKey,
|
|
|
|
|
|
t,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
const handleUndoAll = useCallback(() => {
|
|
|
|
|
|
const pendingKeys = Object.keys(pendingDataBySection);
|
|
|
|
|
|
if (pendingKeys.length === 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
setPendingDataBySection({});
|
|
|
|
|
|
setUnsavedChanges(false);
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
setEditingProfile({});
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
|
|
|
|
|
setSectionStatusByKey((prev) => {
|
|
|
|
|
|
const updated = { ...prev };
|
|
|
|
|
|
for (const key of pendingKeys) {
|
|
|
|
|
|
const menuKey = pendingKeyToMenuKey(key);
|
|
|
|
|
|
if (menuKey && updated[menuKey]) {
|
|
|
|
|
|
updated[menuKey] = {
|
|
|
|
|
|
...updated[menuKey],
|
|
|
|
|
|
hasChanges: false,
|
|
|
|
|
|
hasValidationErrors: false,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return updated;
|
|
|
|
|
|
});
|
|
|
|
|
|
}, [pendingDataBySection, pendingKeyToMenuKey]);
|
|
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
const handleDialog = useCallback(
|
|
|
|
|
|
(save: boolean) => {
|
|
|
|
|
|
if (unsavedChanges && save) {
|
2026-02-27 18:55:36 +03:00
|
|
|
|
handleSaveAll();
|
2024-04-19 14:34:07 +03:00
|
|
|
|
}
|
|
|
|
|
|
setConfirmationDialogOpen(false);
|
|
|
|
|
|
setUnsavedChanges(false);
|
|
|
|
|
|
},
|
2026-02-27 18:55:36 +03:00
|
|
|
|
[unsavedChanges, handleSaveAll],
|
2024-04-19 14:34:07 +03:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2025-03-03 18:30:52 +03:00
|
|
|
|
if (cameras.length > 0) {
|
|
|
|
|
|
if (!selectedCamera) {
|
|
|
|
|
|
// Set to first enabled camera initially if no selection
|
|
|
|
|
|
const firstEnabledCamera =
|
|
|
|
|
|
cameras.find((cam) => cameraEnabledStates[cam.name]) || cameras[0];
|
|
|
|
|
|
setSelectedCamera(firstEnabledCamera.name);
|
2025-10-08 22:59:21 +03:00
|
|
|
|
} else if (
|
|
|
|
|
|
!cameraEnabledStates[selectedCamera] &&
|
2025-10-13 19:52:08 +03:00
|
|
|
|
pageToggle !== "cameraReview"
|
2025-10-08 22:59:21 +03:00
|
|
|
|
) {
|
2025-03-03 18:30:52 +03:00
|
|
|
|
// Switch to first enabled camera if current one is disabled, unless on "camera settings" page
|
|
|
|
|
|
const firstEnabledCamera =
|
|
|
|
|
|
cameras.find((cam) => cameraEnabledStates[cam.name]) || cameras[0];
|
|
|
|
|
|
if (firstEnabledCamera.name !== selectedCamera) {
|
|
|
|
|
|
setSelectedCamera(firstEnabledCamera.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-04-19 14:34:07 +03:00
|
|
|
|
}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
}, [cameras, selectedCamera, cameraEnabledStates, pageToggle]);
|
2024-04-19 20:17:23 +03:00
|
|
|
|
|
2025-02-10 19:42:35 +03:00
|
|
|
|
useSearchEffect("page", (page: string) => {
|
|
|
|
|
|
if (allSettingsViews.includes(page as SettingsType)) {
|
2025-03-08 19:01:08 +03:00
|
|
|
|
// Restrict viewer to UI settings
|
2025-10-13 19:52:08 +03:00
|
|
|
|
if (
|
|
|
|
|
|
!isAdmin &&
|
|
|
|
|
|
!ALLOWED_VIEWS_FOR_VIEWER.includes(page as SettingsType)
|
|
|
|
|
|
) {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
setPageToggle("uiSettings");
|
2025-03-08 19:01:08 +03:00
|
|
|
|
} else {
|
2025-10-08 22:59:21 +03:00
|
|
|
|
setPageToggle(page as SettingsType);
|
2025-03-08 19:01:08 +03:00
|
|
|
|
}
|
2025-11-05 17:49:31 +03:00
|
|
|
|
if (isMobile) {
|
|
|
|
|
|
setContentMobileOpen(true);
|
|
|
|
|
|
}
|
2025-02-10 19:42:35 +03:00
|
|
|
|
}
|
2025-02-15 16:56:45 +03:00
|
|
|
|
// don't clear url params if we're creating a new object mask
|
2025-07-07 17:03:57 +03:00
|
|
|
|
return !(searchParams.has("object_mask") || searchParams.has("event_id"));
|
2025-02-10 19:42:35 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
useSearchEffect("camera", (camera: string) => {
|
|
|
|
|
|
const cameraNames = cameras.map((c) => c.name);
|
|
|
|
|
|
if (cameraNames.includes(camera)) {
|
|
|
|
|
|
setSelectedCamera(camera);
|
2025-11-05 17:49:31 +03:00
|
|
|
|
if (isMobile) {
|
|
|
|
|
|
setContentMobileOpen(true);
|
|
|
|
|
|
}
|
2025-02-10 19:42:35 +03:00
|
|
|
|
}
|
2025-07-07 17:03:57 +03:00
|
|
|
|
// don't clear url params if we're creating a new object mask or trigger
|
|
|
|
|
|
return !(searchParams.has("object_mask") || searchParams.has("event_id"));
|
2025-02-10 19:42:35 +03:00
|
|
|
|
});
|
|
|
|
|
|
|
2024-04-27 20:02:01 +03:00
|
|
|
|
useEffect(() => {
|
2025-10-08 22:59:21 +03:00
|
|
|
|
if (!contentMobileOpen) {
|
|
|
|
|
|
document.title = t("documentTitle.default");
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [t, contentMobileOpen]);
|
2024-04-27 20:02:01 +03:00
|
|
|
|
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
// Profile state handlers
|
|
|
|
|
|
const handleSelectProfile = useCallback(
|
|
|
|
|
|
(camera: string, _section: string, profile: string | null) => {
|
|
|
|
|
|
setEditingProfile((prev) => {
|
|
|
|
|
|
if (profile === null) {
|
|
|
|
|
|
const { [camera]: _, ...rest } = prev;
|
|
|
|
|
|
return rest;
|
|
|
|
|
|
}
|
|
|
|
|
|
return { ...prev, [camera]: profile };
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
[],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const handleDeleteProfileSection = useCallback(
|
|
|
|
|
|
async (camera: string, section: string, profile: string) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await axios.put("config/set", {
|
|
|
|
|
|
requires_restart: 0,
|
|
|
|
|
|
config_data: {
|
|
|
|
|
|
cameras: {
|
|
|
|
|
|
[camera]: {
|
|
|
|
|
|
profiles: {
|
|
|
|
|
|
[profile]: {
|
|
|
|
|
|
[section]: "",
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
await mutate("config");
|
|
|
|
|
|
// Switch back to base config
|
|
|
|
|
|
handleSelectProfile(camera, section, null);
|
|
|
|
|
|
toast.success(
|
|
|
|
|
|
t("profiles.deleteSectionSuccess", {
|
|
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
section: t(`configForm.sections.${section}`, {
|
|
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
defaultValue: section,
|
|
|
|
|
|
}),
|
|
|
|
|
|
profile: profileFriendlyNames.get(profile) ?? profile,
|
|
|
|
|
|
}),
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
toast.error(t("toast.save.error.title", { ns: "common" }));
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
[handleSelectProfile, profileFriendlyNames, t],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const profileState: ProfileState = useMemo(
|
|
|
|
|
|
() => ({
|
|
|
|
|
|
editingProfile,
|
|
|
|
|
|
allProfileNames,
|
|
|
|
|
|
profileFriendlyNames,
|
|
|
|
|
|
onSelectProfile: handleSelectProfile,
|
|
|
|
|
|
onDeleteProfileSection: handleDeleteProfileSection,
|
|
|
|
|
|
}),
|
|
|
|
|
|
[
|
|
|
|
|
|
editingProfile,
|
|
|
|
|
|
allProfileNames,
|
|
|
|
|
|
profileFriendlyNames,
|
|
|
|
|
|
handleSelectProfile,
|
|
|
|
|
|
handleDeleteProfileSection,
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Header profile dropdown: derive section key from current page
|
|
|
|
|
|
const currentSectionKey = useMemo(
|
|
|
|
|
|
() => REVERSE_CAMERA_SECTION_MAPPING[pageToggle] ?? null,
|
|
|
|
|
|
[pageToggle],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const headerEditingProfile = useMemo(() => {
|
|
|
|
|
|
if (!selectedCamera || !currentSectionKey) return null;
|
|
|
|
|
|
return editingProfile[selectedCamera] ?? null;
|
|
|
|
|
|
}, [selectedCamera, currentSectionKey, editingProfile]);
|
|
|
|
|
|
|
|
|
|
|
|
const showProfileDropdown =
|
|
|
|
|
|
PROFILE_DROPDOWN_PAGES.has(pageToggle) &&
|
|
|
|
|
|
!!selectedCamera &&
|
|
|
|
|
|
(allProfileNames.length > 0 || profilesUIEnabled);
|
|
|
|
|
|
|
|
|
|
|
|
const headerHasProfileData = useCallback(
|
|
|
|
|
|
(profileName: string): boolean => {
|
|
|
|
|
|
if (!config || !selectedCamera || !currentSectionKey) return false;
|
|
|
|
|
|
const profileData =
|
|
|
|
|
|
config.cameras[selectedCamera]?.profiles?.[profileName];
|
|
|
|
|
|
if (!profileData) return false;
|
|
|
|
|
|
|
|
|
|
|
|
if (currentSectionKey === "masksAndZones") {
|
|
|
|
|
|
const hasZones =
|
|
|
|
|
|
profileData.zones && Object.keys(profileData.zones).length > 0;
|
|
|
|
|
|
const hasMotionMasks =
|
|
|
|
|
|
profileData.motion?.mask &&
|
|
|
|
|
|
Object.keys(profileData.motion.mask).length > 0;
|
|
|
|
|
|
const hasObjectMasks =
|
|
|
|
|
|
(profileData.objects?.mask &&
|
|
|
|
|
|
Object.keys(profileData.objects.mask).length > 0) ||
|
|
|
|
|
|
(profileData.objects?.filters &&
|
|
|
|
|
|
Object.values(profileData.objects.filters).some(
|
|
|
|
|
|
(f) => f.mask && Object.keys(f.mask).length > 0,
|
|
|
|
|
|
));
|
|
|
|
|
|
return !!(hasZones || hasMotionMasks || hasObjectMasks);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return !!profileData[currentSectionKey as keyof typeof profileData];
|
|
|
|
|
|
},
|
|
|
|
|
|
[config, selectedCamera, currentSectionKey],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const handleDeleteProfileForCurrentSection = useCallback(
|
|
|
|
|
|
async (profileName: string) => {
|
|
|
|
|
|
if (!selectedCamera || !currentSectionKey) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (currentSectionKey === "masksAndZones") {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const profileData =
|
|
|
|
|
|
config?.cameras?.[selectedCamera]?.profiles?.[profileName];
|
|
|
|
|
|
if (!profileData) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Build a targeted delete payload that only removes mask-related
|
|
|
|
|
|
// sub-keys, not the entire motion/objects sections
|
|
|
|
|
|
const deletePayload: JsonObject = {};
|
|
|
|
|
|
|
|
|
|
|
|
if (profileData.zones !== undefined) {
|
|
|
|
|
|
deletePayload.zones = "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (profileData.motion?.mask !== undefined) {
|
|
|
|
|
|
deletePayload.motion = { mask: "" };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (profileData.objects) {
|
|
|
|
|
|
const objDelete: JsonObject = {};
|
|
|
|
|
|
if (profileData.objects.mask !== undefined) {
|
|
|
|
|
|
objDelete.mask = "";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (profileData.objects.filters) {
|
|
|
|
|
|
const filtersDelete: JsonObject = {};
|
|
|
|
|
|
for (const [filterName, filterVal] of Object.entries(
|
|
|
|
|
|
profileData.objects.filters,
|
|
|
|
|
|
)) {
|
|
|
|
|
|
if (filterVal.mask !== undefined) {
|
|
|
|
|
|
filtersDelete[filterName] = { mask: "" };
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Object.keys(filtersDelete).length > 0) {
|
|
|
|
|
|
objDelete.filters = filtersDelete;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Object.keys(objDelete).length > 0) {
|
|
|
|
|
|
deletePayload.objects = objDelete;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Object.keys(deletePayload).length === 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
await axios.put("config/set", {
|
|
|
|
|
|
requires_restart: 0,
|
|
|
|
|
|
config_data: {
|
|
|
|
|
|
cameras: {
|
|
|
|
|
|
[selectedCamera]: {
|
|
|
|
|
|
profiles: {
|
|
|
|
|
|
[profileName]: deletePayload,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
await mutate("config");
|
|
|
|
|
|
handleSelectProfile(selectedCamera, "masksAndZones", null);
|
|
|
|
|
|
toast.success(
|
|
|
|
|
|
t("profiles.deleteSectionSuccess", {
|
|
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
section: t("configForm.sections.masksAndZones", {
|
|
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
}),
|
|
|
|
|
|
profile: profileFriendlyNames.get(profileName) ?? profileName,
|
|
|
|
|
|
}),
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
toast.error(t("toast.save.error.title", { ns: "common" }));
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await handleDeleteProfileSection(
|
|
|
|
|
|
selectedCamera,
|
|
|
|
|
|
currentSectionKey,
|
|
|
|
|
|
profileName,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
[
|
|
|
|
|
|
selectedCamera,
|
|
|
|
|
|
currentSectionKey,
|
|
|
|
|
|
config,
|
|
|
|
|
|
handleSelectProfile,
|
|
|
|
|
|
handleDeleteProfileSection,
|
|
|
|
|
|
profileFriendlyNames,
|
|
|
|
|
|
t,
|
|
|
|
|
|
],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
const handleSectionStatusChange = useCallback(
|
|
|
|
|
|
(sectionKey: string, level: "global" | "camera", status: SectionStatus) => {
|
|
|
|
|
|
// Map section keys to menu keys based on level
|
|
|
|
|
|
let menuKey: string;
|
|
|
|
|
|
if (level === "camera") {
|
|
|
|
|
|
menuKey = CAMERA_SECTION_MAPPING[sectionKey] || sectionKey;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
menuKey =
|
|
|
|
|
|
GLOBAL_SECTION_MAPPING[sectionKey] ||
|
|
|
|
|
|
ENRICHMENTS_SECTION_MAPPING[sectionKey] ||
|
|
|
|
|
|
SYSTEM_SECTION_MAPPING[sectionKey] ||
|
|
|
|
|
|
sectionKey;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setSectionStatusByKey((prev) => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
[menuKey]: status,
|
|
|
|
|
|
}));
|
|
|
|
|
|
},
|
|
|
|
|
|
[],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const handlePendingDataChange = useCallback(
|
|
|
|
|
|
(
|
|
|
|
|
|
sectionKey: string,
|
|
|
|
|
|
cameraName: string | undefined,
|
|
|
|
|
|
data: ConfigSectionData | null,
|
|
|
|
|
|
) => {
|
|
|
|
|
|
const pendingDataKey = cameraName
|
|
|
|
|
|
? `${cameraName}::${sectionKey}`
|
|
|
|
|
|
: sectionKey;
|
|
|
|
|
|
|
|
|
|
|
|
setPendingDataBySection((prev) => {
|
|
|
|
|
|
if (data === null) {
|
|
|
|
|
|
const { [pendingDataKey]: _, ...rest } = prev;
|
|
|
|
|
|
return rest;
|
|
|
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
[pendingDataKey]: data,
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
[],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-03-26 21:47:24 +03:00
|
|
|
|
const handleSectionSavingChange = useCallback((saving: boolean) => {
|
|
|
|
|
|
setIsAnySectionSaving(saving);
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
// The active profile being edited for the selected camera
|
|
|
|
|
|
const activeEditingProfile = selectedCamera
|
|
|
|
|
|
? (editingProfile[selectedCamera] ?? null)
|
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
|
|
// Profile color for the active editing profile
|
|
|
|
|
|
const activeProfileColor = useMemo(
|
|
|
|
|
|
() =>
|
|
|
|
|
|
activeEditingProfile
|
|
|
|
|
|
? getProfileColor(activeEditingProfile, allProfileNames)
|
|
|
|
|
|
: undefined,
|
|
|
|
|
|
[activeEditingProfile, allProfileNames],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
// Initialize override status for all camera sections
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!selectedCamera || !cameraOverrides) return;
|
|
|
|
|
|
|
|
|
|
|
|
const overrideMap: Partial<
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
Record<
|
|
|
|
|
|
SettingsType,
|
|
|
|
|
|
Pick<SectionStatus, "hasChanges" | "isOverridden" | "overrideSource">
|
|
|
|
|
|
>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
> = {};
|
|
|
|
|
|
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
// Build a set of menu keys that have pending changes for this camera
|
|
|
|
|
|
const pendingMenuKeys = new Set<string>();
|
|
|
|
|
|
const cameraPrefix = `${selectedCamera}::`;
|
|
|
|
|
|
for (const key of Object.keys(pendingDataBySection)) {
|
|
|
|
|
|
if (key.startsWith(cameraPrefix)) {
|
|
|
|
|
|
const menuKey = pendingKeyToMenuKey(key);
|
|
|
|
|
|
if (menuKey) pendingMenuKeys.add(menuKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Get profile data if a profile is being edited
|
|
|
|
|
|
const profileData = activeEditingProfile
|
|
|
|
|
|
? config?.cameras?.[selectedCamera]?.profiles?.[activeEditingProfile]
|
|
|
|
|
|
: undefined;
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
// Set override status for all camera sections using the shared mapping
|
|
|
|
|
|
Object.entries(CAMERA_SECTION_MAPPING).forEach(
|
|
|
|
|
|
([sectionKey, settingsKey]) => {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
const globalOverridden = cameraOverrides.includes(sectionKey);
|
|
|
|
|
|
|
|
|
|
|
|
// Check if the active profile overrides this section
|
|
|
|
|
|
const profileOverrides = profileData
|
|
|
|
|
|
? !!profileData[sectionKey as keyof typeof profileData]
|
|
|
|
|
|
: false;
|
|
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
overrideMap[settingsKey] = {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
hasChanges: pendingMenuKeys.has(settingsKey),
|
|
|
|
|
|
isOverridden: profileOverrides || globalOverridden,
|
|
|
|
|
|
overrideSource: profileOverrides
|
|
|
|
|
|
? "profile"
|
|
|
|
|
|
: globalOverridden
|
|
|
|
|
|
? "global"
|
|
|
|
|
|
: undefined,
|
2026-02-27 18:55:36 +03:00
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
setSectionStatusByKey((prev) => {
|
|
|
|
|
|
// Merge and update both hasChanges and isOverridden for camera sections
|
|
|
|
|
|
const merged = { ...prev };
|
|
|
|
|
|
Object.entries(overrideMap).forEach(([key, status]) => {
|
|
|
|
|
|
const existingStatus = merged[key as SettingsType];
|
|
|
|
|
|
merged[key as SettingsType] = {
|
|
|
|
|
|
hasChanges: status.hasChanges,
|
|
|
|
|
|
isOverridden: status.isOverridden,
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
overrideSource: status.overrideSource,
|
2026-02-27 18:55:36 +03:00
|
|
|
|
hasValidationErrors: existingStatus?.hasValidationErrors ?? false,
|
|
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
return merged;
|
|
|
|
|
|
});
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
}, [
|
|
|
|
|
|
selectedCamera,
|
|
|
|
|
|
cameraOverrides,
|
|
|
|
|
|
pendingDataBySection,
|
|
|
|
|
|
pendingKeyToMenuKey,
|
|
|
|
|
|
activeEditingProfile,
|
|
|
|
|
|
config,
|
|
|
|
|
|
]);
|
2026-02-27 18:55:36 +03:00
|
|
|
|
|
|
|
|
|
|
const renderMenuItemLabel = useCallback(
|
|
|
|
|
|
(key: SettingsType) => {
|
|
|
|
|
|
const status = sectionStatusByKey[key];
|
|
|
|
|
|
const showOverrideDot =
|
|
|
|
|
|
CAMERA_SECTION_KEYS.has(key) && status?.isOverridden;
|
|
|
|
|
|
const showUnsavedDot = status?.hasChanges;
|
|
|
|
|
|
|
2026-05-19 06:52:40 +03:00
|
|
|
|
const isProfileOverride =
|
|
|
|
|
|
status?.overrideSource === "profile" && activeProfileColor;
|
|
|
|
|
|
const dotColor = isProfileOverride
|
|
|
|
|
|
? activeProfileColor.dot
|
|
|
|
|
|
: "bg-selected";
|
|
|
|
|
|
|
|
|
|
|
|
const overrideTooltip = isProfileOverride
|
|
|
|
|
|
? t("menuDot.overrideProfile", {
|
|
|
|
|
|
profile: activeEditingProfile
|
|
|
|
|
|
? (profileFriendlyNames.get(activeEditingProfile) ??
|
|
|
|
|
|
activeEditingProfile)
|
|
|
|
|
|
: "",
|
|
|
|
|
|
})
|
|
|
|
|
|
: t("menuDot.overrideGlobal");
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
|
2026-02-27 18:55:36 +03:00
|
|
|
|
return (
|
2026-03-23 16:48:02 +03:00
|
|
|
|
<div className="flex w-full min-w-0 items-center justify-between pr-4 md:pr-0">
|
|
|
|
|
|
<div className="min-w-0 flex-1 whitespace-normal break-words">
|
|
|
|
|
|
{t("menu." + key)}
|
|
|
|
|
|
</div>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{(showOverrideDot || showUnsavedDot) && (
|
2026-03-23 16:48:02 +03:00
|
|
|
|
<div className="ml-2 flex shrink-0 items-center gap-2">
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{showOverrideDot && (
|
2026-05-19 06:52:40 +03:00
|
|
|
|
<Tooltip>
|
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
|
<span
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"inline-block size-2 rounded-full",
|
|
|
|
|
|
dotColor,
|
|
|
|
|
|
)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
|
<TooltipPortal>
|
|
|
|
|
|
<TooltipContent side="right">
|
|
|
|
|
|
{overrideTooltip}
|
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
|
</TooltipPortal>
|
|
|
|
|
|
</Tooltip>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
)}
|
|
|
|
|
|
{showUnsavedDot && (
|
2026-05-19 06:52:40 +03:00
|
|
|
|
<Tooltip>
|
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
|
<span className="inline-block size-2 rounded-full bg-unsaved" />
|
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
|
<TooltipPortal>
|
|
|
|
|
|
<TooltipContent side="right">
|
|
|
|
|
|
{t("menuDot.unsaved")}
|
|
|
|
|
|
</TooltipContent>
|
|
|
|
|
|
</TooltipPortal>
|
|
|
|
|
|
</Tooltip>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
2026-05-19 06:52:40 +03:00
|
|
|
|
[
|
|
|
|
|
|
sectionStatusByKey,
|
|
|
|
|
|
t,
|
|
|
|
|
|
activeProfileColor,
|
|
|
|
|
|
activeEditingProfile,
|
|
|
|
|
|
profileFriendlyNames,
|
|
|
|
|
|
],
|
2026-02-27 18:55:36 +03:00
|
|
|
|
);
|
|
|
|
|
|
|
2025-10-08 22:59:21 +03:00
|
|
|
|
if (isMobile) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<Toaster position="top-center" />
|
2025-10-08 22:59:21 +03:00
|
|
|
|
{!contentMobileOpen && (
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div
|
|
|
|
|
|
key={`mobile-menu-${selectedCamera}`}
|
|
|
|
|
|
className="flex size-full flex-col"
|
|
|
|
|
|
>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
<div className="sticky -top-2 z-50 mb-2 bg-background p-4">
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div className="relative flex w-full items-center justify-center">
|
2025-10-08 22:59:21 +03:00
|
|
|
|
<Logo className="h-8" />
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div className="absolute right-0">
|
|
|
|
|
|
<CameraSelectButton
|
|
|
|
|
|
allCameras={cameras}
|
|
|
|
|
|
selectedCamera={selectedCamera}
|
|
|
|
|
|
setSelectedCamera={setSelectedCamera}
|
|
|
|
|
|
cameraEnabledStates={cameraEnabledStates}
|
|
|
|
|
|
currentPage={page}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</div>
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
<div className="flex flex-row items-center">
|
2025-10-09 15:23:03 +03:00
|
|
|
|
<h2 className="ml-2 text-lg">
|
2025-10-09 00:36:23 +03:00
|
|
|
|
{t("menu.settings", { ns: "common" })}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</h2>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="scrollbar-container overflow-y-auto px-4">
|
|
|
|
|
|
{settingsGroups.map((group) => {
|
|
|
|
|
|
const filteredItems = group.items.filter((item) =>
|
|
|
|
|
|
visibleSettingsViews.includes(item.key as SettingsType),
|
|
|
|
|
|
);
|
|
|
|
|
|
if (filteredItems.length === 0) return null;
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div key={group.label} className="mb-3">
|
|
|
|
|
|
{filteredItems.length > 1 && (
|
|
|
|
|
|
<h3 className="mb-2 ml-2 text-sm font-medium text-secondary-foreground">
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div>{t("menu." + group.label)}</div>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</h3>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{filteredItems.map((item) => (
|
|
|
|
|
|
<MobileMenuItem
|
|
|
|
|
|
key={item.key}
|
|
|
|
|
|
item={item}
|
|
|
|
|
|
className={cn(filteredItems.length == 1 && "pl-2")}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
label={renderMenuItemLabel(item.key as SettingsType)}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
onSelect={(key) => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!isAdmin &&
|
2025-10-13 19:52:08 +03:00
|
|
|
|
!ALLOWED_VIEWS_FOR_VIEWER.includes(
|
|
|
|
|
|
key as SettingsType,
|
|
|
|
|
|
)
|
2025-10-08 22:59:21 +03:00
|
|
|
|
) {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
setPageToggle("uiSettings");
|
2025-10-08 22:59:21 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
setPageToggle(key as SettingsType);
|
|
|
|
|
|
}
|
|
|
|
|
|
setContentMobileOpen(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
})}
|
|
|
|
|
|
</div>
|
2026-04-07 16:16:19 +03:00
|
|
|
|
{showSaveAllButtons && (
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div className="sticky bottom-0 z-50 mt-2 bg-background p-4">
|
|
|
|
|
|
<div className="flex flex-col items-center gap-2">
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
2026-04-22 18:19:30 +03:00
|
|
|
|
<span className="text-sm text-unsaved">
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{t("unsavedChanges", {
|
|
|
|
|
|
ns: "views/settings",
|
|
|
|
|
|
defaultValue: "You have unsaved changes",
|
|
|
|
|
|
})}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<SaveAllPreviewPopover
|
|
|
|
|
|
items={pendingChangesPreview}
|
|
|
|
|
|
className="h-7 w-7"
|
|
|
|
|
|
align="center"
|
|
|
|
|
|
side="top"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleUndoAll}
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
size="sm"
|
2026-03-26 21:47:24 +03:00
|
|
|
|
disabled={isSavingAll || isAnySectionSaving}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
className="flex w-full items-center justify-center gap-2"
|
|
|
|
|
|
>
|
|
|
|
|
|
{t("button.undoAll", {
|
|
|
|
|
|
ns: "common",
|
|
|
|
|
|
defaultValue: "Undo All",
|
|
|
|
|
|
})}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleSaveAll}
|
|
|
|
|
|
variant="select"
|
|
|
|
|
|
size="sm"
|
2026-03-26 21:47:24 +03:00
|
|
|
|
disabled={
|
|
|
|
|
|
isSavingAll ||
|
|
|
|
|
|
isAnySectionSaving ||
|
|
|
|
|
|
hasPendingValidationErrors
|
|
|
|
|
|
}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
className="flex w-full items-center justify-center gap-2"
|
|
|
|
|
|
>
|
|
|
|
|
|
{isSavingAll ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<ActivityIndicator className="h-4 w-4" />
|
|
|
|
|
|
{t("button.savingAll", { ns: "common" })}
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
t("button.saveAll", { ns: "common" })
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2024-04-19 20:17:23 +03:00
|
|
|
|
</div>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
)}
|
|
|
|
|
|
<MobilePage
|
|
|
|
|
|
open={contentMobileOpen}
|
|
|
|
|
|
onOpenChange={setContentMobileOpen}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MobilePageContent
|
|
|
|
|
|
className={cn("px-2", "scrollbar-container overflow-y-auto")}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MobilePageHeader
|
|
|
|
|
|
className="top-0 mb-0"
|
|
|
|
|
|
onClose={() => navigate(-1)}
|
|
|
|
|
|
actions={
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
{CAMERA_SELECT_BUTTON_PAGES.includes(pageToggle) && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{pageToggle == "masksAndZones" && (
|
|
|
|
|
|
<ZoneMaskFilterButton
|
|
|
|
|
|
selectedZoneMask={filterZoneMask}
|
|
|
|
|
|
updateZoneMaskFilter={setFilterZoneMask}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
{showProfileDropdown && currentSectionKey && (
|
|
|
|
|
|
<ProfileSectionDropdown
|
|
|
|
|
|
allProfileNames={allProfileNames}
|
|
|
|
|
|
profileFriendlyNames={profileFriendlyNames}
|
|
|
|
|
|
editingProfile={headerEditingProfile}
|
|
|
|
|
|
hasProfileData={headerHasProfileData}
|
|
|
|
|
|
onSelectProfile={(profile) =>
|
|
|
|
|
|
handleSelectProfile(
|
|
|
|
|
|
selectedCamera,
|
|
|
|
|
|
currentSectionKey,
|
|
|
|
|
|
profile,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
iconOnly
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<CameraSelectButton
|
|
|
|
|
|
allCameras={cameras}
|
|
|
|
|
|
selectedCamera={selectedCamera}
|
|
|
|
|
|
setSelectedCamera={setSelectedCamera}
|
|
|
|
|
|
cameraEnabledStates={cameraEnabledStates}
|
|
|
|
|
|
currentPage={page}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
/>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<MobilePageTitle>{t("menu." + page)}</MobilePageTitle>
|
|
|
|
|
|
</MobilePageHeader>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="p-2">
|
|
|
|
|
|
{(() => {
|
|
|
|
|
|
const CurrentComponent = getCurrentComponent(page);
|
|
|
|
|
|
if (!CurrentComponent) return null;
|
|
|
|
|
|
return (
|
|
|
|
|
|
<CurrentComponent
|
|
|
|
|
|
selectedCamera={selectedCamera}
|
|
|
|
|
|
setUnsavedChanges={setUnsavedChanges}
|
|
|
|
|
|
selectedZoneMask={filterZoneMask}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
onSectionStatusChange={handleSectionStatusChange}
|
|
|
|
|
|
pendingDataBySection={pendingDataBySection}
|
|
|
|
|
|
onPendingDataChange={handlePendingDataChange}
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
profileState={profileState}
|
|
|
|
|
|
onDeleteProfileSection={
|
|
|
|
|
|
handleDeleteProfileForCurrentSection
|
|
|
|
|
|
}
|
|
|
|
|
|
profilesUIEnabled={profilesUIEnabled}
|
|
|
|
|
|
setProfilesUIEnabled={setProfilesUIEnabled}
|
2026-03-26 21:47:24 +03:00
|
|
|
|
isSavingAll={isSavingAll}
|
|
|
|
|
|
onSectionSavingChange={handleSectionSavingChange}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
/>
|
|
|
|
|
|
);
|
|
|
|
|
|
})()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</MobilePageContent>
|
|
|
|
|
|
</MobilePage>
|
|
|
|
|
|
{confirmationDialogOpen && (
|
|
|
|
|
|
<AlertDialog
|
|
|
|
|
|
open={confirmationDialogOpen}
|
|
|
|
|
|
onOpenChange={() => setConfirmationDialogOpen(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<AlertDialogContent>
|
|
|
|
|
|
<AlertDialogHeader>
|
|
|
|
|
|
<AlertDialogTitle>
|
|
|
|
|
|
{t("dialog.unsavedChanges.title")}
|
|
|
|
|
|
</AlertDialogTitle>
|
|
|
|
|
|
<AlertDialogDescription>
|
|
|
|
|
|
{t("dialog.unsavedChanges.desc")}
|
|
|
|
|
|
</AlertDialogDescription>
|
|
|
|
|
|
</AlertDialogHeader>
|
|
|
|
|
|
<AlertDialogFooter>
|
|
|
|
|
|
<AlertDialogCancel onClick={() => handleDialog(false)}>
|
|
|
|
|
|
{t("button.cancel", { ns: "common" })}
|
|
|
|
|
|
</AlertDialogCancel>
|
|
|
|
|
|
<AlertDialogAction onClick={() => handleDialog(true)}>
|
|
|
|
|
|
{t("button.save", { ns: "common" })}
|
|
|
|
|
|
</AlertDialogAction>
|
|
|
|
|
|
</AlertDialogFooter>
|
|
|
|
|
|
</AlertDialogContent>
|
|
|
|
|
|
</AlertDialog>
|
|
|
|
|
|
)}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<RestartDialog
|
|
|
|
|
|
isOpen={restartDialogOpen}
|
|
|
|
|
|
onClose={() => setRestartDialogOpen(false)}
|
|
|
|
|
|
onRestart={() => sendRestart("restart")}
|
|
|
|
|
|
/>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="flex h-full flex-col">
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<Toaster position="top-center" />
|
2025-12-15 21:42:11 +03:00
|
|
|
|
<div className="flex min-h-16 items-center justify-between border-b border-secondary p-3">
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
<div className="mr-2 flex w-full items-center justify-between gap-3">
|
|
|
|
|
|
<Heading as="h3" className="mb-0">
|
|
|
|
|
|
{t("menu.settings", { ns: "common" })}
|
|
|
|
|
|
</Heading>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
2026-04-07 16:16:19 +03:00
|
|
|
|
{showSaveAllButtons && (
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"flex flex-row items-center gap-2",
|
|
|
|
|
|
CAMERA_SELECT_BUTTON_PAGES.includes(page) &&
|
|
|
|
|
|
"border-r border-secondary pr-5",
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<SaveAllPreviewPopover
|
|
|
|
|
|
items={pendingChangesPreview}
|
|
|
|
|
|
className="size-8"
|
|
|
|
|
|
align="end"
|
|
|
|
|
|
side="bottom"
|
2024-04-19 14:34:07 +03:00
|
|
|
|
/>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleUndoAll}
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
size="sm"
|
2026-03-26 21:47:24 +03:00
|
|
|
|
disabled={isSavingAll || isAnySectionSaving}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
className="flex items-center justify-center gap-2"
|
|
|
|
|
|
>
|
|
|
|
|
|
{t("button.undoAll", {
|
|
|
|
|
|
ns: "common",
|
|
|
|
|
|
defaultValue: "Undo All",
|
|
|
|
|
|
})}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="select"
|
|
|
|
|
|
size="sm"
|
|
|
|
|
|
onClick={handleSaveAll}
|
2026-03-26 21:47:24 +03:00
|
|
|
|
disabled={
|
|
|
|
|
|
isSavingAll ||
|
|
|
|
|
|
isAnySectionSaving ||
|
|
|
|
|
|
hasPendingValidationErrors
|
|
|
|
|
|
}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
className="flex items-center justify-center gap-2"
|
|
|
|
|
|
>
|
|
|
|
|
|
{isSavingAll ? (
|
|
|
|
|
|
<>
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
<ActivityIndicator className="mr-2 size-4" />
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{t("button.savingAll", { ns: "common" })}
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
t("button.saveAll", { ns: "common" })
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{CAMERA_SELECT_BUTTON_PAGES.includes(page) && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{pageToggle == "masksAndZones" && (
|
|
|
|
|
|
<ZoneMaskFilterButton
|
|
|
|
|
|
selectedZoneMask={filterZoneMask}
|
|
|
|
|
|
updateZoneMaskFilter={setFilterZoneMask}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
{showProfileDropdown && currentSectionKey && (
|
|
|
|
|
|
<ProfileSectionDropdown
|
|
|
|
|
|
allProfileNames={allProfileNames}
|
|
|
|
|
|
profileFriendlyNames={profileFriendlyNames}
|
|
|
|
|
|
editingProfile={headerEditingProfile}
|
|
|
|
|
|
hasProfileData={headerHasProfileData}
|
|
|
|
|
|
onSelectProfile={(profile) =>
|
|
|
|
|
|
handleSelectProfile(
|
|
|
|
|
|
selectedCamera,
|
|
|
|
|
|
currentSectionKey,
|
|
|
|
|
|
profile,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<CameraSelectButton
|
|
|
|
|
|
allCameras={cameras}
|
|
|
|
|
|
selectedCamera={selectedCamera}
|
|
|
|
|
|
setSelectedCamera={setSelectedCamera}
|
|
|
|
|
|
cameraEnabledStates={cameraEnabledStates}
|
|
|
|
|
|
currentPage={page}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
2024-04-19 14:34:07 +03:00
|
|
|
|
</div>
|
2026-03-06 23:45:39 +03:00
|
|
|
|
<SidebarProvider className="relative h-full min-h-0 flex-1">
|
|
|
|
|
|
<Sidebar variant="inset" className="absolute h-full pl-0 pt-0">
|
|
|
|
|
|
<SidebarContent className="scrollbar-container overflow-y-auto border-r-[1px] border-secondary bg-background py-2">
|
2025-10-08 22:59:21 +03:00
|
|
|
|
<SidebarMenu>
|
|
|
|
|
|
{settingsGroups.map((group) => {
|
|
|
|
|
|
const filteredItems = group.items.filter((item) =>
|
|
|
|
|
|
visibleSettingsViews.includes(item.key as SettingsType),
|
|
|
|
|
|
);
|
|
|
|
|
|
if (filteredItems.length === 0) return null;
|
|
|
|
|
|
return (
|
|
|
|
|
|
<SidebarGroup key={group.label} className="py-1">
|
|
|
|
|
|
{filteredItems.length === 1 ? (
|
|
|
|
|
|
<SidebarMenu>
|
|
|
|
|
|
<SidebarMenuItem>
|
|
|
|
|
|
<SidebarMenuButton
|
2026-03-23 16:48:02 +03:00
|
|
|
|
className="ml-0 h-auto min-h-8 py-1.5"
|
2025-10-08 22:59:21 +03:00
|
|
|
|
isActive={pageToggle === filteredItems[0].key}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!isAdmin &&
|
2025-10-13 19:52:08 +03:00
|
|
|
|
!ALLOWED_VIEWS_FOR_VIEWER.includes(
|
2025-10-08 22:59:21 +03:00
|
|
|
|
filteredItems[0].key as SettingsType,
|
|
|
|
|
|
)
|
|
|
|
|
|
) {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
setPageToggle("uiSettings");
|
2025-10-08 22:59:21 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
setPageToggle(
|
|
|
|
|
|
filteredItems[0].key as SettingsType,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
{renderMenuItemLabel(
|
|
|
|
|
|
filteredItems[0].key as SettingsType,
|
|
|
|
|
|
)}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</SidebarMenuButton>
|
|
|
|
|
|
</SidebarMenuItem>
|
|
|
|
|
|
</SidebarMenu>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<SidebarGroupLabel
|
|
|
|
|
|
className={cn(
|
2025-10-09 00:02:38 +03:00
|
|
|
|
"ml-2 cursor-default pl-0 text-sm",
|
2025-10-08 22:59:21 +03:00
|
|
|
|
filteredItems.some(
|
|
|
|
|
|
(item) => pageToggle === item.key,
|
|
|
|
|
|
)
|
|
|
|
|
|
? "text-primary"
|
|
|
|
|
|
: "text-sidebar-foreground/80",
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div>{t("menu." + group.label)}</div>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</SidebarGroupLabel>
|
2025-10-09 00:02:38 +03:00
|
|
|
|
<SidebarMenuSub className="mx-2 border-0">
|
2025-10-08 22:59:21 +03:00
|
|
|
|
{filteredItems.map((item) => (
|
|
|
|
|
|
<SidebarMenuSubItem key={item.key}>
|
|
|
|
|
|
<SidebarMenuSubButton
|
2026-03-23 16:48:02 +03:00
|
|
|
|
className="h-auto w-full py-1.5"
|
2025-10-08 22:59:21 +03:00
|
|
|
|
isActive={pageToggle === item.key}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!isAdmin &&
|
2025-10-13 19:52:08 +03:00
|
|
|
|
!ALLOWED_VIEWS_FOR_VIEWER.includes(
|
2025-10-08 22:59:21 +03:00
|
|
|
|
item.key as SettingsType,
|
|
|
|
|
|
)
|
|
|
|
|
|
) {
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
setPageToggle("uiSettings");
|
2025-10-08 22:59:21 +03:00
|
|
|
|
} else {
|
|
|
|
|
|
setPageToggle(item.key as SettingsType);
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div className="w-full cursor-pointer">
|
|
|
|
|
|
{renderMenuItemLabel(
|
|
|
|
|
|
item.key as SettingsType,
|
|
|
|
|
|
)}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
</SidebarMenuSubButton>
|
|
|
|
|
|
</SidebarMenuSubItem>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</SidebarMenuSub>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</SidebarGroup>
|
|
|
|
|
|
);
|
|
|
|
|
|
})}
|
|
|
|
|
|
</SidebarMenu>
|
|
|
|
|
|
</SidebarContent>
|
|
|
|
|
|
</Sidebar>
|
|
|
|
|
|
<SidebarInset>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<div
|
|
|
|
|
|
className={cn(
|
2026-03-06 23:45:39 +03:00
|
|
|
|
"scrollbar-container flex-1 overflow-y-auto pl-2 pr-0 pt-2",
|
2026-02-27 18:55:36 +03:00
|
|
|
|
)}
|
|
|
|
|
|
>
|
2025-10-08 22:59:21 +03:00
|
|
|
|
{(() => {
|
|
|
|
|
|
const CurrentComponent = getCurrentComponent(page);
|
|
|
|
|
|
if (!CurrentComponent) return null;
|
|
|
|
|
|
return (
|
|
|
|
|
|
<CurrentComponent
|
|
|
|
|
|
selectedCamera={selectedCamera}
|
|
|
|
|
|
setUnsavedChanges={setUnsavedChanges}
|
|
|
|
|
|
selectedZoneMask={filterZoneMask}
|
2026-02-27 18:55:36 +03:00
|
|
|
|
onSectionStatusChange={handleSectionStatusChange}
|
|
|
|
|
|
pendingDataBySection={pendingDataBySection}
|
|
|
|
|
|
onPendingDataChange={handlePendingDataChange}
|
Camera profile support (#22482)
* add CameraProfileConfig model for named config overrides
* add profiles field to CameraConfig
* add active_profile field to FrigateConfig
Runtime-only field excluded from YAML serialization, tracks which
profile is currently active.
* add ProfileManager for profile activation and persistence
Handles snapshotting base configs, applying profile overrides via
deep_merge + apply_section_update, publishing ZMQ updates, and
persisting active profile to /config/.active_profile.
* add profile API endpoints (GET /profiles, GET/PUT /profile)
* add MQTT and dispatcher integration for profiles
- Subscribe to frigate/profile/set MQTT topic
- Publish profile/state and profiles/available on connect
- Add _on_profile_command handler to dispatcher
- Broadcast active profile state on WebSocket connect
* wire ProfileManager into app startup and FastAPI
- Create ProfileManager after dispatcher init
- Restore persisted profile on startup
- Pass dispatcher and profile_manager to FastAPI app
* add tests for invalid profile values and keys
Tests that Pydantic rejects: invalid field values (fps: "not_a_number"),
unknown section keys (ffmpeg in profile), invalid nested values, and
invalid profiles in full config parsing.
* formatting
* fix CameraLiveConfig JSON serialization error on profile activation
refactor _publish_updates to only publish ZMQ updates for
sections that actually changed, not all sections on affected cameras.
* consolidate
* add enabled field to camera profiles for enabling/disabling cameras
* add zones support to camera profiles
* add frontend profile types, color utility, and config save support
* add profile state management and save preview support
* add profileName prop to BaseSection for profile-aware config editing
* add profile section dropdown and wire into camera settings pages
* add per-profile camera enable/disable to Camera Management view
* add profiles summary page with card-based layout and fix backend zone comparison bug
* add active profile badge to settings toolbar
* i18n
* add red dot for any pending changes including profiles
* profile support for mask and zone editor
* fix hidden field validation errors caused by lodash wildcard and schema gaps
lodash unset does not support wildcard (*) segments, so hidden fields like
filters.*.mask were never stripped from form data, leaving null raw_coordinates
that fail RJSF anyOf validation. Add unsetWithWildcard helper and also strip
hidden fields from the JSON schema itself as defense-in-depth.
* add face_recognition and lpr to profile-eligible sections
* move profile dropdown from section panes to settings header
* add profiles enable toggle and improve empty state
* formatting
* tweaks
* tweak colors and switch
* fix profile save diff, masksAndZones delete, and config sync
* ui tweaks
* ensure profile manager gets updated config
* rename profile settings to ui settings
* refactor profilesview and add dots/border colors when overridden
* implement an update_config method for profile manager
* fix mask deletion
* more unique colors
* add top-level profiles config section with friendly names
* implement profile friendly names and improve profile UI
- Add ProfileDefinitionConfig type and profiles field to FrigateConfig
- Use ProfilesApiResponse type with friendly_name support throughout
- Replace Record<string, unknown> with proper JsonObject/JsonValue types
- Add profile creation form matching zone pattern (Zod + NameAndIdFields)
- Add pencil icon for renaming profile friendly names in ProfilesView
- Move Profiles menu item to first under Camera Configuration
- Add activity indicators on save/rename/delete buttons
- Display friendly names in CameraManagementView profile selector
- Fix duplicate colored dots in management profile dropdown
- Fix i18n namespace for overridden base config tooltips
- Move profile override deletion from dropdown trash icon to footer
button with confirmation dialog, matching Reset to Global pattern
- Remove Add Profile from section header dropdown to prevent saving
camera overrides before top-level profile definition exists
- Clean up newProfiles state after API profile deletion
- Refresh profiles SWR cache after saving profile definitions
* remove profile badge in settings and add profiles to main menu
* use icon only on mobile
* change color order
* docs
* show activity indicator on trash icon while deleting a profile
* tweak language
* immediately create profiles on backend instead of deferring to Save All
* hide restart-required fields when editing a profile section
fields that require a restart cannot take effect via profile switching,
so they are merged into hiddenFields when profileName is set
* show active profile indicator in desktop status bar
* fix profile config inheritance bug where Pydantic defaults override base values
The /config API was dumping profile overrides with model_dump() which included
all Pydantic defaults. When the frontend merged these over
the camera's base config, explicitly-set base values were
lost. Now profile overrides are re-dumped with exclude_unset=True so only
user-specified fields are returned.
Also fixes the Save All path generating spurious deletion markers for
restart-required fields that are hidden during profile
editing but not excluded from the raw data sanitization in
prepareSectionSavePayload.
* docs tweaks
* docs tweak
* formatting
* formatting
* fix typing
* fix test pollution
test_maintainer was injecting MagicMock() into sys.modules["frigate.config.camera.updater"] at module load time and never restoring it. When the profile tests later imported CameraConfigUpdateEnum and CameraConfigUpdateTopic from that module, they got mock objects instead of the real dataclass/enum, so equality comparisons always failed
* remove
* fix settings showing profile-merged values when editing base config
When a profile is active, the in-memory config contains effective
(profile-merged) values. The settings UI was displaying these merged
values even when the "Base Config" view was selected.
Backend: snapshot pre-profile base configs in ProfileManager and expose
them via a `base_config` key in the /api/config camera response when a
profile is active. The top-level sections continue to reflect the
effective running config.
Frontend: read from `base_config` when available in BaseSection,
useConfigOverride, useAllCameraOverrides, and prepareSectionSavePayload.
Include formData labels in Object/Audio switches widgets so that labels
added only by a profile override remain visible when editing that profile.
* use rasterized_mask as field
makes it easier to exclude from the schema with exclude=True
prevents leaking of the field when using model_dump for profiles
* fix zones
- Fix zone colors not matching across profiles by falling back to base zone color when profile zone data lacks a color field
- Use base_config for base-layer values in masks/zones view so profile-merged values don't pollute the base config editing view
- Handle zones separately in profile manager snapshot/restore since ZoneConfig requires special serialization (color as private attr, contour generation)
- Inherit base zone color and generate contours for profile zone overrides in profile manager
* formatting
* don't require restart for camera enabled change for profiles
* publish camera state when changing profiles
* formatting
* remove available profiles from mqtt
* improve typing
2026-03-19 17:47:57 +03:00
|
|
|
|
profileState={profileState}
|
|
|
|
|
|
onDeleteProfileSection={handleDeleteProfileForCurrentSection}
|
|
|
|
|
|
profilesUIEnabled={profilesUIEnabled}
|
|
|
|
|
|
setProfilesUIEnabled={setProfilesUIEnabled}
|
2026-03-26 21:47:24 +03:00
|
|
|
|
isSavingAll={isSavingAll}
|
|
|
|
|
|
onSectionSavingChange={handleSectionSavingChange}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
/>
|
|
|
|
|
|
);
|
|
|
|
|
|
})()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SidebarInset>
|
|
|
|
|
|
{confirmationDialogOpen && (
|
|
|
|
|
|
<AlertDialog
|
|
|
|
|
|
open={confirmationDialogOpen}
|
|
|
|
|
|
onOpenChange={() => setConfirmationDialogOpen(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<AlertDialogContent>
|
|
|
|
|
|
<AlertDialogHeader>
|
|
|
|
|
|
<AlertDialogTitle>
|
|
|
|
|
|
{t("dialog.unsavedChanges.title")}
|
|
|
|
|
|
</AlertDialogTitle>
|
|
|
|
|
|
<AlertDialogDescription>
|
|
|
|
|
|
{t("dialog.unsavedChanges.desc")}
|
|
|
|
|
|
</AlertDialogDescription>
|
|
|
|
|
|
</AlertDialogHeader>
|
|
|
|
|
|
<AlertDialogFooter>
|
|
|
|
|
|
<AlertDialogCancel onClick={() => handleDialog(false)}>
|
|
|
|
|
|
{t("button.cancel", { ns: "common" })}
|
|
|
|
|
|
</AlertDialogCancel>
|
|
|
|
|
|
<AlertDialogAction onClick={() => handleDialog(true)}>
|
|
|
|
|
|
{t("button.save", { ns: "common" })}
|
|
|
|
|
|
</AlertDialogAction>
|
|
|
|
|
|
</AlertDialogFooter>
|
|
|
|
|
|
</AlertDialogContent>
|
|
|
|
|
|
</AlertDialog>
|
2025-03-24 18:19:58 +03:00
|
|
|
|
)}
|
2025-10-08 22:59:21 +03:00
|
|
|
|
</SidebarProvider>
|
2026-02-27 18:55:36 +03:00
|
|
|
|
<RestartDialog
|
|
|
|
|
|
isOpen={restartDialogOpen}
|
|
|
|
|
|
onClose={() => setRestartDialogOpen(false)}
|
|
|
|
|
|
onRestart={() => sendRestart("restart")}
|
|
|
|
|
|
/>
|
2024-04-19 14:34:07 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type CameraSelectButtonProps = {
|
|
|
|
|
|
allCameras: CameraConfig[];
|
|
|
|
|
|
selectedCamera: string;
|
|
|
|
|
|
setSelectedCamera: React.Dispatch<React.SetStateAction<string>>;
|
2025-03-03 18:30:52 +03:00
|
|
|
|
cameraEnabledStates: Record<string, boolean>;
|
|
|
|
|
|
currentPage: SettingsType;
|
2024-04-19 14:34:07 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function CameraSelectButton({
|
|
|
|
|
|
allCameras,
|
|
|
|
|
|
selectedCamera,
|
|
|
|
|
|
setSelectedCamera,
|
2025-03-03 18:30:52 +03:00
|
|
|
|
cameraEnabledStates,
|
|
|
|
|
|
currentPage,
|
2024-04-19 14:34:07 +03:00
|
|
|
|
}: CameraSelectButtonProps) {
|
2025-03-16 18:36:20 +03:00
|
|
|
|
const { t } = useTranslation(["views/settings"]);
|
|
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
const [open, setOpen] = useState(false);
|
2023-12-08 16:33:22 +03:00
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
if (!allCameras.length) {
|
2025-03-03 18:30:52 +03:00
|
|
|
|
return null;
|
2024-04-19 14:34:07 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const trigger = (
|
|
|
|
|
|
<Button
|
2025-04-23 01:21:09 +03:00
|
|
|
|
className="flex items-center gap-2 bg-selected smart-capitalize hover:bg-selected"
|
2024-10-23 01:07:42 +03:00
|
|
|
|
aria-label="Select a camera"
|
2024-04-19 14:34:07 +03:00
|
|
|
|
size="sm"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaVideo className="text-background dark:text-primary" />
|
2024-05-14 18:06:44 +03:00
|
|
|
|
<div className="hidden text-background dark:text-primary md:block">
|
2025-08-26 20:15:01 +03:00
|
|
|
|
{selectedCamera == undefined ? (
|
|
|
|
|
|
t("cameraSetting.noCamera")
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<CameraNameLabel camera={selectedCamera} />
|
|
|
|
|
|
)}
|
2024-04-19 14:34:07 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
);
|
|
|
|
|
|
const content = (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{isMobile && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<DropdownMenuLabel className="flex justify-center">
|
2025-03-16 18:36:20 +03:00
|
|
|
|
{t("cameraSetting.camera")}
|
2024-04-19 14:34:07 +03:00
|
|
|
|
</DropdownMenuLabel>
|
|
|
|
|
|
<DropdownMenuSeparator />
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
2024-06-03 21:43:30 +03:00
|
|
|
|
<div className="scrollbar-container mb-5 h-auto max-h-[80dvh] overflow-y-auto overflow-x-hidden p-4 md:mb-1">
|
2024-04-19 14:34:07 +03:00
|
|
|
|
<div className="flex flex-col gap-2.5">
|
2025-03-03 18:30:52 +03:00
|
|
|
|
{allCameras.map((item) => {
|
|
|
|
|
|
const isEnabled = cameraEnabledStates[item.name];
|
2025-10-13 19:52:08 +03:00
|
|
|
|
const isCameraSettingsPage = currentPage === "cameraReview";
|
2025-03-03 18:30:52 +03:00
|
|
|
|
return (
|
|
|
|
|
|
<FilterSwitch
|
|
|
|
|
|
key={item.name}
|
|
|
|
|
|
isChecked={item.name === selectedCamera}
|
2025-08-26 20:15:01 +03:00
|
|
|
|
label={item.name}
|
2025-11-07 17:02:06 +03:00
|
|
|
|
type={"camera"}
|
2025-03-03 18:30:52 +03:00
|
|
|
|
onCheckedChange={(isChecked) => {
|
|
|
|
|
|
if (isChecked && (isEnabled || isCameraSettingsPage)) {
|
|
|
|
|
|
setSelectedCamera(item.name);
|
|
|
|
|
|
setOpen(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
disabled={!isEnabled && !isCameraSettingsPage}
|
|
|
|
|
|
/>
|
|
|
|
|
|
);
|
|
|
|
|
|
})}
|
2024-04-19 14:34:07 +03:00
|
|
|
|
</div>
|
2023-12-08 16:33:22 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2024-04-19 14:34:07 +03:00
|
|
|
|
if (isMobile) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Drawer
|
|
|
|
|
|
open={open}
|
|
|
|
|
|
onOpenChange={(open: boolean) => {
|
|
|
|
|
|
if (!open) {
|
|
|
|
|
|
setSelectedCamera(selectedCamera);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setOpen(open);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
|
|
|
|
|
|
<DrawerContent className="max-h-[75dvh] overflow-hidden">
|
|
|
|
|
|
{content}
|
|
|
|
|
|
</DrawerContent>
|
|
|
|
|
|
</Drawer>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<DropdownMenu
|
|
|
|
|
|
open={open}
|
|
|
|
|
|
onOpenChange={(open: boolean) => {
|
|
|
|
|
|
if (!open) {
|
|
|
|
|
|
setSelectedCamera(selectedCamera);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setOpen(open);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
|
|
|
|
|
<DropdownMenuContent>{content}</DropdownMenuContent>
|
|
|
|
|
|
</DropdownMenu>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|