Full UI configuration (#22151)

* use react-jsonschema-form for UI config

* don't use properties wrapper when generating config i18n json

* configure for full i18n support

* section fields

* add descriptions to all fields for i18n

* motion i18n

* fix nullable fields

* sanitize internal fields

* add switches widgets and use friendly names

* fix nullable schema entries

* ensure update_topic is added to api calls

this needs further backend implementation to work correctly

* add global sections, camera config overrides, and reset button

* i18n

* add reset logic to global config view

* tweaks

* fix sections and live validation

* fix validation for schema objects that can be null

* generic and custom per-field validation

* improve generic error validation messages

* remove show advanced fields switch

* tweaks

* use shadcn theme

* fix array field template

* i18n tweaks

* remove collapsible around root section

* deep merge schema for advanced fields

* add array field item template and fix ffmpeg section

* add missing i18n keys

* tweaks

* comment out api call for testing

* add config groups as a separate i18n namespace

* add descriptions to all pydantic fields

* make titles more concise

* new titles as i18n

* update i18n config generation script to use json schema

* tweaks

* tweaks

* rebase

* clean up

* form tweaks

* add wildcards and fix object filter fields

* add field template for additionalproperties schema objects

* improve typing

* add section description from schema and clarify global vs camera level descriptions

* separate and consolidate global and camera i18n namespaces

* clean up now obsolete namespaces

* tweaks

* refactor sections and overrides

* add ability to render components before and after fields

* fix titles

* chore(sections): remove legacy single-section components replaced by template

* refactor configs to use individual files with a template

* fix review description

* apply hidden fields after ui schema

* move util

* remove unused i18n

* clean up error messages

* fix fast refresh

* add custom validation and use it for ffmpeg input roles

* update nav tree

* remove unused

* re-add override and modified indicators

* mark pending changes and add confirmation dialog for resets

* fix red unsaved dot

* tweaks

* add docs links, readonly keys, and restart required per field

* add special case and comments for global motion section

* add section form special cases

* combine review sections

* tweaks

* add audio labels endpoint

* add audio label switches and input to filter list

* fix type

* remove key from config when resetting to default/global

* don't show description for new key/val fields

* tweaks

* spacing tweaks

* add activity indicator and scrollbar tweaks

* add docs to filter fields

* wording changes

* fix global ffmpeg section

* add review classification zones to review form

* add backend endpoint and frontend widget for ffmpeg presets and manual args

* improve wording

* hide descriptions for additional properties arrays

* add warning log about incorrectly nested model config

* spacing and language tweaks

* fix i18n keys

* networking section docs and description

* small wording tweaks

* add layout grid field

* refactor with shared utilities

* field order

* add individual detectors to schema

add detector titles and descriptions (docstrings in pydantic are used for descriptions) and add i18n keys to globals

* clean up detectors section and i18n

* don't save model config back to yaml when saving detectors

* add full detectors config to api model dump

works around the way we use detector plugins so we can have the full detector config for the frontend

* add restart button to toast when restart is required

* add ui option to remove inner cards

* fix buttons

* section tweaks

* don't zoom into text on mobile

* make buttons sticky at bottom of sections

* small tweaks

* highlight label of changed fields

* add null to enum list when unwrapping

* refactor to shared utils and add save all button

* add undo all button

* add RJSF to dictionary

* consolidate utils

* preserve form data when changing cameras

* add mono fonts

* add popover to show what fields will be saved

* fix mobile menu not re-rendering with unsaved dots

* tweaks

* fix logger and env vars config section saving

use escaped periods in keys to retain them in the config file (eg "frigate.embeddings")

* add timezone widget

* role map field with validation

* fix validation for model section

* add another hidden field

* add footer message for required restart

* use rjsf for notifications view

* fix config saving

* add replace rules field

* default column layout and add field sizing

* clean up field template

* refactor profile settings to match rjsf forms

* tweaks

* refactor frigate+ view and make tweaks to sections

* show frigate+ model info in detection model settings when using a frigate+ model

* update restartRequired for all fields

* fix restart fields

* tweaks and add ability enable disabled cameras

more backend changes required

* require restart when enabling camera that is disabled in config

* disable save when form is invalid

* refactor ffmpeg section for readability

* change label

* clean up camera inputs fields

* misc tweaks to ffmpeg section

- add raw paths endpoint to ensure credentials get saved
- restart required tooltip

* maintenance settings tweaks

* don't mutate with lodash

* fix description re-rendering for nullable object fields

* hide reindex field

* update rjsf

* add frigate+ description to settings pane

* disable save all when any section is invalid

* show translated field name in validation error pane

* clean up

* remove unused

* fix genai merge

* fix genai
This commit is contained in:
Josh Hawkins
2026-02-27 08:55:36 -07:00
committed by GitHub
parent eeefbf2bb5
commit e7250f24cb
206 changed files with 22200 additions and 4435 deletions
@@ -0,0 +1,42 @@
import type { SectionConfigOverrides } from "./types";
const audio: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/audio_detectors",
restartRequired: [],
fieldOrder: [
"enabled",
"listen",
"filters",
"min_volume",
"max_not_heard",
"num_threads",
],
fieldGroups: {
detection: ["enabled", "listen", "filters"],
sensitivity: ["min_volume", "max_not_heard"],
},
hiddenFields: ["enabled_in_config"],
advancedFields: ["min_volume", "max_not_heard", "num_threads"],
uiSchema: {
listen: {
"ui:widget": "audioLabels",
},
},
},
global: {
restartRequired: [
"enabled",
"listen",
"filters",
"min_volume",
"max_not_heard",
"num_threads",
],
},
camera: {
restartRequired: ["num_threads"],
},
};
export default audio;
@@ -0,0 +1,19 @@
import type { SectionConfigOverrides } from "./types";
const audioTranscription: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/audio_detectors#audio-transcription",
restartRequired: [],
fieldOrder: ["enabled", "language", "device", "model_size"],
hiddenFields: ["enabled_in_config", "live_enabled"],
advancedFields: ["language", "device", "model_size"],
overrideFields: ["enabled", "live_enabled"],
},
global: {
fieldOrder: ["enabled", "language", "device", "model_size"],
advancedFields: ["language", "device", "model_size"],
restartRequired: ["enabled", "language", "device", "model_size"],
},
};
export default audioTranscription;
@@ -0,0 +1,49 @@
import type { SectionConfigOverrides } from "./types";
const auth: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/authentication",
restartRequired: [
"enabled",
"reset_admin_password",
"failed_login_rate_limit",
],
fieldOrder: [
"enabled",
"reset_admin_password",
"cookie_name",
"cookie_secure",
"session_length",
"refresh_time",
"native_oauth_url",
"failed_login_rate_limit",
"trusted_proxies",
"hash_iterations",
"roles",
],
hiddenFields: ["admin_first_time_login", "roles"],
advancedFields: [
"cookie_name",
"cookie_secure",
"session_length",
"refresh_time",
"failed_login_rate_limit",
"trusted_proxies",
"hash_iterations",
"roles",
],
uiSchema: {
reset_admin_password: {
"ui:widget": "switch",
},
native_oauth_url: {
"ui:options": { size: "lg" },
},
failed_login_rate_limit: {
"ui:options": { size: "md" },
},
},
},
};
export default auth;
@@ -0,0 +1,45 @@
import type { SectionConfigOverrides } from "./types";
const birdseye: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/birdseye",
restartRequired: [],
fieldOrder: ["enabled", "mode", "order"],
hiddenFields: [],
advancedFields: [],
overrideFields: ["enabled", "mode"],
},
global: {
fieldOrder: [
"enabled",
"restream",
"width",
"height",
"quality",
"mode",
"layout",
"inactivity_threshold",
"idle_heartbeat_fps",
],
advancedFields: ["width", "height", "quality", "inactivity_threshold"],
restartRequired: [
"enabled",
"restream",
"width",
"height",
"quality",
"mode",
"layout.scaling_factor",
"inactivity_threshold",
"layout.max_cameras",
"idle_heartbeat_fps",
],
uiSchema: {
mode: {
"ui:size": "xs",
},
},
},
};
export default birdseye;
@@ -0,0 +1,12 @@
import type { SectionConfigOverrides } from "./types";
const classification: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/custom_classification/object_classification",
restartRequired: ["bird.enabled", "bird.threshold"],
hiddenFields: ["custom"],
advancedFields: [],
},
};
export default classification;
@@ -0,0 +1,17 @@
import type { SectionConfigOverrides } from "./types";
const database: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/advanced#database",
restartRequired: ["path"],
fieldOrder: ["path"],
advancedFields: [],
uiSchema: {
path: {
"ui:options": { size: "md" },
},
},
},
};
export default database;
@@ -0,0 +1,49 @@
import type { SectionConfigOverrides } from "./types";
const detect: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/camera_specific",
fieldOrder: [
"enabled",
"width",
"height",
"fps",
"min_initialized",
"max_disappeared",
"annotation_offset",
"stationary",
"interval",
"threshold",
"max_frames",
],
restartRequired: [],
fieldGroups: {
resolution: ["enabled", "width", "height", "fps"],
tracking: ["min_initialized", "max_disappeared"],
},
hiddenFields: ["enabled_in_config"],
advancedFields: [
"min_initialized",
"max_disappeared",
"annotation_offset",
"stationary",
],
},
global: {
restartRequired: [
"enabled",
"width",
"height",
"fps",
"min_initialized",
"max_disappeared",
"annotation_offset",
"stationary",
],
},
camera: {
restartRequired: ["width", "height", "min_initialized", "max_disappeared"],
},
};
export default detect;
@@ -0,0 +1,28 @@
import type { SectionConfigOverrides } from "./types";
const detectorHiddenFields = [
"*.model.labelmap",
"*.model.attributes_map",
"*.model",
"*.model_path",
];
const detectors: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/object_detectors",
fieldOrder: [],
advancedFields: [],
hiddenFields: detectorHiddenFields,
uiSchema: {
"ui:field": "DetectorHardwareField",
"ui:options": {
multiInstanceTypes: ["cpu", "onnx", "openvino"],
typeOrder: ["onnx", "openvino", "edgetpu"],
hiddenByType: {},
hiddenFields: detectorHiddenFields,
},
},
},
};
export default detectors;
@@ -0,0 +1,16 @@
import type { SectionConfigOverrides } from "./types";
const environmentVars: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/advanced#environment_vars",
fieldOrder: [],
advancedFields: [],
uiSchema: {
additionalProperties: {
"ui:options": { size: "lg" },
},
},
},
};
export default environmentVars;
@@ -0,0 +1,50 @@
import type { SectionConfigOverrides } from "./types";
const faceRecognition: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/face_recognition",
restartRequired: [],
fieldOrder: ["enabled", "min_area"],
hiddenFields: [],
advancedFields: ["min_area"],
overrideFields: ["enabled", "min_area"],
},
global: {
fieldOrder: [
"enabled",
"model_size",
"unknown_score",
"detection_threshold",
"recognition_threshold",
"min_area",
"min_faces",
"save_attempts",
"blur_confidence_filter",
"device",
],
advancedFields: [
"unknown_score",
"detection_threshold",
"recognition_threshold",
"min_area",
"min_faces",
"save_attempts",
"blur_confidence_filter",
"device",
],
restartRequired: [
"enabled",
"model_size",
"unknown_score",
"detection_threshold",
"recognition_threshold",
"min_area",
"min_faces",
"save_attempts",
"blur_confidence_filter",
"device",
],
},
};
export default faceRecognition;
@@ -0,0 +1,179 @@
import type { SectionConfigOverrides } from "./types";
const arrayAsTextWidget = {
"ui:widget": "ArrayAsTextWidget",
"ui:options": {
suppressMultiSchema: true,
},
};
const ffmpegArgsWidget = (
presetField: string,
extraOptions?: Record<string, unknown>,
) => ({
"ui:widget": "FfmpegArgsWidget",
"ui:options": {
suppressMultiSchema: true,
ffmpegPresetField: presetField,
...extraOptions,
},
});
const ffmpeg: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/ffmpeg_presets",
fieldDocs: {
hwaccel_args: "/configuration/ffmpeg_presets#hwaccel-presets",
"inputs.hwaccel_args": "/configuration/ffmpeg_presets#hwaccel-presets",
input_args: "/configuration/ffmpeg_presets#input-args-presets",
"inputs.input_args": "/configuration/ffmpeg_presets#input-args-presets",
output_args: "/configuration/ffmpeg_presets#output-args-presets",
"inputs.output_args": "/configuration/ffmpeg_presets#output-args-presets",
"output_args.record": "/configuration/ffmpeg_presets#output-args-presets",
"inputs.roles": "/configuration/cameras/#setting-up-camera-inputs",
},
restartRequired: [],
fieldOrder: [
"inputs",
"global_args",
"input_args",
"hwaccel_args",
"output_args",
"path",
"retry_interval",
"apple_compatibility",
"gpu",
],
hiddenFields: [],
advancedFields: [
"path",
"global_args",
"retry_interval",
"apple_compatibility",
"gpu",
],
overrideFields: [
"inputs",
"path",
"global_args",
"input_args",
"hwaccel_args",
"output_args",
"retry_interval",
"apple_compatibility",
"gpu",
],
uiSchema: {
path: {
"ui:options": { size: "md" },
},
global_args: arrayAsTextWidget,
hwaccel_args: ffmpegArgsWidget("hwaccel_args"),
input_args: ffmpegArgsWidget("input_args"),
output_args: {
detect: arrayAsTextWidget,
record: ffmpegArgsWidget("output_args.record"),
items: {
detect: arrayAsTextWidget,
record: ffmpegArgsWidget("output_args.record"),
},
},
inputs: {
"ui:field": "CameraInputsField",
items: {
path: {
"ui:options": { size: "full" },
},
roles: {
"ui:widget": "inputRoles",
"ui:options": {
showArrayItemDescription: true,
},
},
global_args: {
"ui:widget": "hidden",
},
hwaccel_args: ffmpegArgsWidget("hwaccel_args", {
allowInherit: true,
hideDescription: true,
forceSplitLayout: true,
showArrayItemDescription: true,
}),
input_args: ffmpegArgsWidget("input_args", {
allowInherit: true,
hideDescription: true,
forceSplitLayout: true,
showArrayItemDescription: true,
}),
output_args: {
items: {
detect: arrayAsTextWidget,
record: ffmpegArgsWidget("output_args.record"),
},
},
},
},
},
},
global: {
restartRequired: [
"path",
"global_args",
"hwaccel_args",
"input_args",
"output_args",
"retry_interval",
"apple_compatibility",
"gpu",
],
fieldOrder: [
"hwaccel_args",
"path",
"global_args",
"input_args",
"output_args",
"retry_interval",
"apple_compatibility",
"gpu",
],
advancedFields: [
"global_args",
"input_args",
"output_args",
"path",
"retry_interval",
"apple_compatibility",
"gpu",
],
uiSchema: {
path: {
"ui:options": { size: "md" },
},
global_args: arrayAsTextWidget,
hwaccel_args: ffmpegArgsWidget("hwaccel_args"),
input_args: ffmpegArgsWidget("input_args"),
output_args: {
detect: arrayAsTextWidget,
record: ffmpegArgsWidget("output_args.record"),
},
},
},
camera: {
fieldGroups: {
cameraFfmpeg: ["input_args", "hwaccel_args", "output_args"],
},
restartRequired: [
"inputs",
"path",
"global_args",
"hwaccel_args",
"input_args",
"output_args",
"retry_interval",
"apple_compatibility",
"gpu",
],
},
};
export default ffmpeg;
@@ -0,0 +1,48 @@
import type { SectionConfigOverrides } from "./types";
const genai: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/genai/config",
restartRequired: [
"provider",
"api_key",
"base_url",
"model",
"provider_options",
"runtime_options",
],
fieldOrder: [
"provider",
"api_key",
"base_url",
"model",
"provider_options",
"runtime_options",
],
advancedFields: ["base_url", "provider_options", "runtime_options"],
hiddenFields: ["genai.enabled_in_config"],
uiSchema: {
api_key: {
"ui:options": { size: "md" },
},
base_url: {
"ui:options": { size: "lg" },
},
model: {
"ui:options": { size: "md" },
},
provider_options: {
additionalProperties: {
"ui:options": { size: "lg" },
},
},
runtime_options: {
additionalProperties: {
"ui:options": { size: "lg" },
},
},
},
},
};
export default genai;
@@ -0,0 +1,21 @@
import type { SectionConfigOverrides } from "./types";
const live: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/live",
restartRequired: [],
fieldOrder: ["stream_name", "height", "quality"],
fieldGroups: {},
hiddenFields: ["enabled_in_config"],
advancedFields: ["height", "quality"],
},
global: {
restartRequired: ["stream_name", "height", "quality"],
hiddenFields: ["streams"],
},
camera: {
restartRequired: ["height", "quality"],
},
};
export default live;
@@ -0,0 +1,12 @@
import type { SectionConfigOverrides } from "./types";
const logger: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/advanced#logger",
restartRequired: ["default", "logs"],
fieldOrder: ["default", "logs"],
advancedFields: ["logs"],
},
};
export default logger;
@@ -0,0 +1,73 @@
import type { SectionConfigOverrides } from "./types";
const lpr: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/license_plate_recognition",
fieldDocs: {
enhancement: "/configuration/license_plate_recognition#enhancement",
},
restartRequired: [],
fieldOrder: ["enabled", "expire_time", "min_area", "enhancement"],
hiddenFields: [],
advancedFields: ["expire_time", "min_area", "enhancement"],
overrideFields: ["enabled", "min_area", "enhancement"],
},
global: {
fieldOrder: [
"enabled",
"model_size",
"detection_threshold",
"min_area",
"recognition_threshold",
"min_plate_length",
"format",
"match_distance",
"known_plates",
"enhancement",
"debug_save_plates",
"device",
"replace_rules",
],
advancedFields: [
"detection_threshold",
"recognition_threshold",
"min_plate_length",
"format",
"match_distance",
"known_plates",
"enhancement",
"debug_save_plates",
"device",
"replace_rules",
],
restartRequired: [
"enabled",
"model_size",
"detection_threshold",
"min_area",
"recognition_threshold",
"min_plate_length",
"format",
"match_distance",
"known_plates",
"enhancement",
"debug_save_plates",
"device",
"replace_rules",
],
uiSchema: {
format: {
"ui:options": { size: "md" },
},
replace_rules: {
"ui:field": "ReplaceRulesField",
"ui:options": {
label: false,
suppressDescription: true,
},
},
},
},
};
export default lpr;
@@ -0,0 +1,53 @@
import type { SectionConfigOverrides } from "./types";
const model: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/object_detectors#model",
restartRequired: [
"path",
"labelmap_path",
"width",
"height",
"labelmap",
"attributes_map",
"input_tensor",
"input_pixel_format",
"input_dtype",
"model_type",
],
fieldOrder: [
"path",
"labelmap_path",
"width",
"height",
"input_pixel_format",
"input_tensor",
"input_dtype",
"model_type",
],
advancedFields: [
"input_pixel_format",
"input_tensor",
"input_dtype",
"model_type",
],
hiddenFields: [
"labelmap",
"attributes_map",
"colormap",
"all_attributes",
"non_logo_attributes",
"plus",
],
uiSchema: {
path: {
"ui:options": { size: "md" },
},
labelmap_path: {
"ui:options": { size: "md" },
},
},
},
};
export default model;
@@ -0,0 +1,49 @@
import type { SectionConfigOverrides } from "./types";
const motion: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/motion_detection",
restartRequired: [],
fieldOrder: [
"enabled",
"threshold",
"lightning_threshold",
"improve_contrast",
"contour_area",
"delta_alpha",
"frame_alpha",
"frame_height",
"mqtt_off_delay",
],
fieldGroups: {
sensitivity: ["enabled", "threshold", "contour_area"],
algorithm: ["improve_contrast", "delta_alpha", "frame_alpha"],
},
hiddenFields: ["enabled_in_config", "mask", "raw_mask"],
advancedFields: [
"lightning_threshold",
"delta_alpha",
"frame_alpha",
"frame_height",
"mqtt_off_delay",
],
},
global: {
restartRequired: [
"enabled",
"threshold",
"lightning_threshold",
"improve_contrast",
"contour_area",
"delta_alpha",
"frame_alpha",
"frame_height",
"mqtt_off_delay",
],
},
camera: {
restartRequired: ["frame_height"],
},
};
export default motion;
@@ -0,0 +1,73 @@
import type { SectionConfigOverrides } from "./types";
const mqtt: SectionConfigOverrides = {
base: {
sectionDocs: "/integrations/mqtt",
restartRequired: [],
fieldOrder: [
"enabled",
"timestamp",
"bounding_box",
"crop",
"height",
"required_zones",
"quality",
],
hiddenFields: [],
advancedFields: ["height", "quality"],
overrideFields: [],
uiSchema: {
required_zones: {
"ui:widget": "zoneNames",
},
},
},
global: {
fieldOrder: [
"enabled",
"host",
"port",
"user",
"password",
"topic_prefix",
"client_id",
"stats_interval",
"qos",
"tls_ca_certs",
"tls_client_cert",
"tls_client_key",
"tls_insecure",
],
advancedFields: [
"stats_interval",
"qos",
"tls_ca_certs",
"tls_client_cert",
"tls_client_key",
"tls_insecure",
],
restartRequired: [
"enabled",
"host",
"port",
"user",
"password",
"topic_prefix",
"client_id",
"stats_interval",
"qos",
"tls_ca_certs",
"tls_client_cert",
"tls_client_key",
"tls_insecure",
],
liveValidate: true,
uiSchema: {
password: {
"ui:options": { size: "xs" },
},
},
},
};
export default mqtt;
@@ -0,0 +1,30 @@
import type { SectionConfigOverrides } from "./types";
const networking: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/advanced",
fieldDocs: {
"listen.internal": "/configuration/advanced#listen-on-different-ports",
"listen.external": "/configuration/advanced#listen-on-different-ports",
},
restartRequired: ["ipv6.enabled", "listen.internal", "listen.external"],
fieldOrder: [],
advancedFields: [],
uiSchema: {
"listen.internal": {
"ui:options": {
suppressMultiSchema: true,
size: "sm",
},
},
"listen.external": {
"ui:options": {
suppressMultiSchema: true,
size: "sm",
},
},
},
},
};
export default networking;
@@ -0,0 +1,26 @@
import type { SectionConfigOverrides } from "./types";
const notifications: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/notifications",
restartRequired: [],
fieldOrder: ["enabled", "email"],
fieldGroups: {},
hiddenFields: ["enabled_in_config"],
advancedFields: [],
},
global: {
uiSchema: {
"ui:before": { render: "NotificationsSettingsExtras" },
enabled: { "ui:widget": "hidden" },
email: { "ui:widget": "hidden" },
cooldown: { "ui:widget": "hidden" },
enabled_in_config: { "ui:widget": "hidden" },
},
},
camera: {
hiddenFields: ["enabled_in_config", "email"],
},
};
export default notifications;
@@ -0,0 +1,104 @@
import type { SectionConfigOverrides } from "./types";
const objects: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/object_filters",
fieldDocs: {
"filters.min_area": "/configuration/object_filters#object-area",
"filters.max_area": "/configuration/object_filters#object-area",
"filters.min_score": "/configuration/object_filters#minimum-score",
"filters.threshold": "/configuration/object_filters#threshold",
"filters.min_ratio": "/configuration/object_filters/#object-proportions",
"filters.max_ratio": "/configuration/object_filters/#object-proportions",
},
restartRequired: [],
fieldOrder: ["track", "alert", "detect", "filters"],
fieldGroups: {
tracking: ["track", "alert", "detect"],
filtering: ["filters"],
},
hiddenFields: [
"enabled_in_config",
"mask",
"raw_mask",
"genai.enabled_in_config",
"filters.*.mask",
"filters.*.raw_mask",
"filters.mask",
"filters.raw_mask",
],
advancedFields: ["genai"],
uiSchema: {
"filters.*.min_area": {
"ui:options": {
suppressMultiSchema: true,
},
},
"filters.*": {
"ui:options": {
additionalPropertyKeyReadonly: true,
},
},
"filters.*.max_area": {
"ui:options": {
suppressMultiSchema: true,
},
},
track: {
"ui:widget": "objectLabels",
"ui:options": {
suppressMultiSchema: true,
},
},
genai: {
objects: {
"ui:widget": "objectLabels",
"ui:options": {
suppressMultiSchema: true,
},
},
prompt: {
"ui:widget": "textarea",
"ui:options": {
size: "full",
},
},
object_prompts: {
additionalProperties: {
"ui:options": {
size: "full",
},
},
},
required_zones: {
"ui:widget": "zoneNames",
"ui:options": {
suppressMultiSchema: true,
},
},
enabled_in_config: {
"ui:widget": "hidden",
},
},
},
},
global: {
restartRequired: ["track", "alert", "detect", "filters", "genai"],
hiddenFields: [
"enabled_in_config",
"mask",
"raw_mask",
"genai.enabled_in_config",
"filters.*.mask",
"filters.*.raw_mask",
"filters.mask",
"filters.raw_mask",
"genai.required_zones",
],
},
camera: {
restartRequired: [],
},
};
export default objects;
@@ -0,0 +1,46 @@
import type { SectionConfigOverrides } from "./types";
const onvif: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/cameras#setting-up-camera-ptz-controls",
restartRequired: [
"host",
"port",
"user",
"password",
"tls_insecure",
"ignore_time_mismatch",
"autotracking.calibrate_on_startup",
],
fieldOrder: [
"host",
"port",
"user",
"password",
"tls_insecure",
"ignore_time_mismatch",
"autotracking",
],
hiddenFields: [
"autotracking.enabled_in_config",
"autotracking.movement_weights",
],
advancedFields: ["tls_insecure", "ignore_time_mismatch"],
overrideFields: [],
uiSchema: {
host: {
"ui:options": { size: "sm" },
},
autotracking: {
required_zones: {
"ui:widget": "zoneNames",
},
track: {
"ui:widget": "objectLabels",
},
},
},
},
};
export default onvif;
@@ -0,0 +1,33 @@
import type { SectionConfigOverrides } from "./types";
const proxy: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/authentication#proxy",
restartRequired: [],
fieldOrder: [
"header_map",
"logout_url",
"auth_secret",
"default_role",
"separator",
],
advancedFields: ["header_map", "auth_secret", "separator"],
liveValidate: true,
uiSchema: {
logout_url: {
"ui:options": { size: "lg" },
},
auth_secret: {
"ui:options": { size: "md" },
},
header_map: {
"ui:after": { render: "ProxyRoleMap" },
},
"header_map.role_map": {
"ui:widget": "hidden",
},
},
},
};
export default proxy;
@@ -0,0 +1,48 @@
import type { SectionConfigOverrides } from "./types";
const record: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/record",
restartRequired: [],
fieldOrder: [
"enabled",
"expire_interval",
"continuous",
"motion",
"alerts",
"detections",
"preview",
"export",
],
fieldGroups: {
retention: ["enabled", "continuous", "motion"],
events: ["alerts", "detections"],
},
hiddenFields: ["enabled_in_config", "sync_recordings"],
advancedFields: ["expire_interval", "preview", "export"],
uiSchema: {
export: {
hwaccel_args: {
"ui:options": { size: "lg" },
},
},
},
},
global: {
restartRequired: [
"enabled",
"expire_interval",
"continuous",
"motion",
"alerts",
"detections",
"preview",
"export",
],
},
camera: {
restartRequired: [],
},
};
export default record;
@@ -0,0 +1,54 @@
import type { SectionConfigOverrides } from "./types";
const review: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/review",
restartRequired: [],
fieldOrder: ["alerts", "detections", "genai"],
fieldGroups: {},
hiddenFields: [
"enabled_in_config",
"alerts.labels",
"alerts.enabled_in_config",
"detections.labels",
"detections.enabled_in_config",
"genai.enabled_in_config",
],
advancedFields: [],
uiSchema: {
alerts: {
"ui:before": { render: "CameraReviewStatusToggles" },
required_zones: {
"ui:widget": "hidden",
},
},
detections: {
required_zones: {
"ui:widget": "hidden",
},
},
genai: {
additional_concerns: {
"ui:widget": "textarea",
"ui:options": {
size: "full",
},
},
activity_context_prompt: {
"ui:widget": "textarea",
"ui:options": {
size: "full",
},
},
},
},
},
global: {
restartRequired: ["alerts", "detections", "genai"],
},
camera: {
restartRequired: [],
},
};
export default review;
@@ -0,0 +1,24 @@
import type { SectionConfigOverrides } from "./types";
const semanticSearch: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/semantic_search",
restartRequired: [],
hiddenFields: [],
advancedFields: [],
overrideFields: [],
uiSchema: {
enabled: {
"ui:after": { render: "SemanticSearchReindex" },
},
},
},
global: {
fieldOrder: ["enabled", "reindex", "model", "model_size", "device"],
advancedFields: ["reindex", "device"],
restartRequired: ["enabled", "model", "model_size", "device"],
hiddenFields: ["reindex"],
},
};
export default semanticSearch;
@@ -0,0 +1,45 @@
import type { SectionConfigOverrides } from "./types";
const snapshots: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/snapshots",
restartRequired: [],
fieldOrder: [
"enabled",
"bounding_box",
"crop",
"quality",
"timestamp",
"retain",
],
fieldGroups: {
display: ["enabled", "bounding_box", "crop", "quality", "timestamp"],
},
hiddenFields: ["enabled_in_config"],
advancedFields: ["height", "quality", "retain"],
uiSchema: {
required_zones: {
"ui:widget": "zoneNames",
"ui:options": {
suppressMultiSchema: true,
},
},
},
},
global: {
restartRequired: [
"enabled",
"bounding_box",
"crop",
"quality",
"timestamp",
"retain",
],
hiddenFields: ["enabled_in_config", "required_zones"],
},
camera: {
restartRequired: [],
},
};
export default snapshots;
@@ -0,0 +1,19 @@
import type { SectionConfigOverrides } from "./types";
const telemetry: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/reference",
restartRequired: [
"network_interfaces",
"stats.amd_gpu_stats",
"stats.intel_gpu_stats",
"stats.intel_gpu_device",
"stats.network_bandwidth",
"version_check",
],
fieldOrder: ["network_interfaces", "stats", "version_check"],
advancedFields: [],
},
};
export default telemetry;
@@ -0,0 +1,27 @@
import type { SectionConfigOverrides } from "./types";
const timestampStyle: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/reference",
restartRequired: [],
fieldOrder: ["position", "format", "color", "thickness"],
hiddenFields: ["effect", "enabled_in_config"],
advancedFields: [],
uiSchema: {
position: {
"ui:size": "xs",
},
format: {
"ui:size": "xs",
},
},
},
global: {
restartRequired: ["position", "format", "color", "thickness", "effect"],
},
camera: {
restartRequired: [],
},
};
export default timestampStyle;
@@ -0,0 +1,20 @@
import type { SectionConfigOverrides } from "./types";
const tls: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/tls",
restartRequired: ["enabled"],
fieldOrder: ["enabled", "cert", "key"],
advancedFields: [],
uiSchema: {
cert: {
"ui:options": { size: "md" },
},
key: {
"ui:options": { size: "md" },
},
},
},
};
export default tls;
@@ -0,0 +1,7 @@
import type { SectionConfig } from "../sections/BaseSection";
export type SectionConfigOverrides = {
base?: SectionConfig;
global?: Partial<SectionConfig>;
camera?: Partial<SectionConfig>;
};
@@ -0,0 +1,30 @@
import type { SectionConfigOverrides } from "./types";
const ui: SectionConfigOverrides = {
base: {
sectionDocs: "/configuration/reference",
restartRequired: [],
fieldOrder: ["dashboard", "order"],
hiddenFields: [],
advancedFields: [],
overrideFields: [],
},
global: {
fieldOrder: [
"timezone",
"time_format",
"date_style",
"time_style",
"unit_system",
],
advancedFields: [],
restartRequired: ["unit_system"],
uiSchema: {
timezone: {
"ui:widget": "timezoneSelect",
},
},
},
};
export default ui;