mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-10 10:33:11 +03:00
clean up
This commit is contained in:
parent
bc5c29fda1
commit
3b5b4bffb9
@ -1,488 +0,0 @@
|
||||
---
|
||||
task: UI Configuration using react-jsonschema-form
|
||||
slug: ui-config-rjsf
|
||||
created: 2026-01-21
|
||||
status: planning
|
||||
---
|
||||
|
||||
# UI Configuration using react-jsonschema-form
|
||||
|
||||
## Overview
|
||||
|
||||
Implement a comprehensive configuration UI for Frigate NVR using react-jsonschema-form (RJSF), driven by the existing Pydantic configuration schema. The UI should allow users to configure Frigate through a web interface instead of manually editing YAML files, with proper validation, reusable components, and clear visual indicators for global vs camera-level settings.
|
||||
|
||||
## Research Findings
|
||||
|
||||
### Key Components
|
||||
|
||||
| Component | Location | Purpose |
|
||||
| ---------------- | --------------------------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| FrigateConfig | [frigate/config/config.py](frigate/config/config.py#L297-L412) | Root Pydantic model defining complete Frigate configuration |
|
||||
| CameraConfig | [frigate/config/camera/camera.py](frigate/config/camera/camera.py#L49-L130) | Camera-level configuration model |
|
||||
| FrigateBaseModel | [frigate/config/base.py](frigate/config/base.py) | Base model with `extra="forbid"` validation |
|
||||
| Config API | [frigate/api/app.py](frigate/api/app.py#L367-L456) | PUT /config/set endpoint for saving config |
|
||||
| Schema Endpoint | [frigate/api/app.py](frigate/api/app.py#L73-L77) | GET /config/schema.json - exposes Pydantic schema |
|
||||
| Settings Page | [web/src/pages/Settings.tsx](web/src/pages/Settings.tsx) | Main settings page with sidebar navigation |
|
||||
| Form Components | [web/src/components/ui/form.tsx](web/src/components/ui/form.tsx) | Existing react-hook-form based form components |
|
||||
|
||||
### Architecture
|
||||
|
||||
The Frigate configuration system is structured as follows:
|
||||
|
||||
1. **Pydantic Models**: All configuration is defined using Pydantic v2 models in `frigate/config/`. The `FrigateConfig` class is the root model.
|
||||
|
||||
2. **Schema Generation**: Pydantic automatically generates JSON Schema via `model_json_schema()`. The schema is already exposed at `/api/config/schema.json` and used by the ConfigEditor (monaco-yaml) for validation.
|
||||
|
||||
3. **Config API**: The `/config/set` endpoint accepts configuration updates in two formats:
|
||||
- Query string parameters (e.g., `?cameras.front.detect.enabled=True`)
|
||||
- JSON body via `config_data` field in `AppConfigSetBody`
|
||||
|
||||
The JSON body is preferred.
|
||||
|
||||
4. **Frontend Stack**:
|
||||
- React 18 with TypeScript
|
||||
- Radix UI primitives + Tailwind CSS (shadcn/ui patterns)
|
||||
- react-hook-form + zod for form validation
|
||||
- SWR for data fetching
|
||||
- react-i18next for translations
|
||||
- axios for API calls
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. **Config Loading**: `useSWR("config")` fetches current config from `/api/config`
|
||||
2. **Schema Loading**: JSON Schema available at `/api/config/schema.json`
|
||||
3. **Config Updates**: PUT to `/config/set` with JSON body
|
||||
4. **Real-time Updates**: Some settings support `requires_restart: 0` for live updates via WebSocket pub/sub
|
||||
|
||||
### Reusable Config Sections (Global + Camera Level)
|
||||
|
||||
Based on schema analysis, these sections appear at BOTH global and camera levels using the **same** Pydantic model:
|
||||
|
||||
| Section | Model | Notes |
|
||||
| --------------- | -------------------- | ------------------------------------------------------------------- |
|
||||
| audio | AudioConfig | Full model reuse |
|
||||
| detect | DetectConfig | Full model reuse, nested StationaryConfig |
|
||||
| live | CameraLiveConfig | Full model reuse |
|
||||
| motion | MotionConfig | Full model reuse |
|
||||
| notifications | NotificationConfig | Full model reuse |
|
||||
| objects | ObjectConfig | Full model reuse, nested FilterConfig, GenAIObjectConfig |
|
||||
| record | RecordConfig | Full model reuse, nested EventsConfig, RetainConfig |
|
||||
| review | ReviewConfig | Full model reuse, nested ReviewAlertsConfig, ReviewDetectionsConfig |
|
||||
| snapshots | SnapshotsConfig | Full model reuse, nested SnapshotsRetainConfig |
|
||||
| timestamp_style | TimestampStyleConfig | Full model reuse, nested ColorConfig |
|
||||
|
||||
**Different models at global vs camera level** (require conditional handling):
|
||||
|
||||
| Section | Global Model | Camera Model | Notes |
|
||||
| ------------------- | ----------------------------- | ----------------------------------- | -------------------- |
|
||||
| audio_transcription | AudioTranscriptionConfig | CameraAudioTranscriptionConfig | Camera has subset |
|
||||
| birdseye | BirdseyeConfig | BirdseyeCameraConfig | Different fields |
|
||||
| face_recognition | FaceRecognitionConfig | CameraFaceRecognitionConfig | Camera has subset |
|
||||
| ffmpeg | FfmpegConfig | CameraFfmpegConfig | Camera adds inputs[] |
|
||||
| lpr | LicensePlateRecognitionConfig | CameraLicensePlateRecognitionConfig | Camera has subset |
|
||||
| semantic_search | SemanticSearchConfig | CameraSemanticSearchConfig | Completely different |
|
||||
|
||||
### Special Field Types Requiring Custom Widgets
|
||||
|
||||
| Type | Examples | Widget Needed |
|
||||
| --------------------- | --------------------------------------------------- | ------------------------------ |
|
||||
| Enums | BirdseyeModeEnum, RetainModeEnum, RecordQualityEnum | Select dropdown |
|
||||
| list[str] | objects.track, zones[], required_zones | Tag input / Multi-select |
|
||||
| Union[str, list[str]] | mask fields | Text or array input |
|
||||
| dict[str, T] | zones, objects.filters | Key-value editor / Nested form |
|
||||
| ColorConfig | timestamp_style.color (RGB) | Color picker |
|
||||
| Coordinates/Masks | zone.coordinates | Polygon editor (existing) |
|
||||
| Password fields | mqtt.password | Password input with show/hide |
|
||||
|
||||
### Current UI Patterns
|
||||
|
||||
1. **Settings Navigation**: [web/src/pages/Settings.tsx](web/src/pages/Settings.tsx#L68-L114) uses a sidebar with grouped sections
|
||||
2. **Camera Selection**: Many views accept `selectedCamera` prop for per-camera settings
|
||||
3. **Form Pattern**: Uses react-hook-form with zod schemas (see [CameraReviewSettingsView.tsx](web/src/views/settings/CameraReviewSettingsView.tsx#L113-L124))
|
||||
4. **Save Pattern**: Axios PUT to `config/set?key=value` with `requires_restart` flag
|
||||
5. **Translations**: All strings in `web/public/locales/{lang}/views/` JSON files
|
||||
|
||||
### Dependencies
|
||||
|
||||
**Internal**:
|
||||
|
||||
- Existing form components: Form, FormField, FormItem, FormLabel, FormControl, FormMessage
|
||||
- UI primitives: Switch, Select, Input, Checkbox, Slider, Tabs
|
||||
- Existing hooks: useSWR, useTranslation, useOptimisticState
|
||||
|
||||
**External (to add)**:
|
||||
|
||||
- @rjsf/core (react-jsonschema-form core)
|
||||
- @rjsf/utils (utilities)
|
||||
- @rjsf/validator-ajv8 (JSON Schema validation)
|
||||
|
||||
### Configuration
|
||||
|
||||
The `/config/set` API expects:
|
||||
|
||||
```typescript
|
||||
interface AppConfigSetBody {
|
||||
requires_restart: number; // 0 = live update, 1 = needs restart
|
||||
update_topic?: string; // For pub/sub notification
|
||||
config_data?: Record<string, any>; // Bulk config updates
|
||||
}
|
||||
```
|
||||
|
||||
Query string format: `?key.path.to.field=value` (e.g., `?cameras.front.detect.enabled=True`)
|
||||
|
||||
### Tests
|
||||
|
||||
| Test File | Coverage |
|
||||
| ---------------------------------------------------- | ------------------------------------- |
|
||||
| No existing React component tests found | RJSF forms would benefit from testing |
|
||||
| Pydantic validation tests implicit in config loading | Schema validation ensures correctness |
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Goal
|
||||
|
||||
Users can configure all user-facing Frigate settings through a form-based UI with validation, clear global vs camera-level distinction, and proper handling of advanced settings.
|
||||
|
||||
### Current State Analysis
|
||||
|
||||
- Schema already exposed at `/api/config/schema.json`
|
||||
- Settings page structure exists with sidebar navigation
|
||||
- Form components exist (react-hook-form based)
|
||||
- No react-jsonschema-form currently installed
|
||||
- Config set API supports both query strings and JSON body
|
||||
|
||||
### What We're NOT Doing
|
||||
|
||||
- Replacing the raw YAML ConfigEditor (remains as advanced option)
|
||||
- Changing the Pydantic models structure
|
||||
- Modifying the config/set API endpoint
|
||||
- Auto-generating translations (manual translation required)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [x] Install @rjsf/core, @rjsf/utils, @rjsf/validator-ajv8
|
||||
- [x] Create exclusion list and advanced settings list JSON files
|
||||
|
||||
---
|
||||
|
||||
## Phases
|
||||
|
||||
| # | Phase | Status | Plan | Notes |
|
||||
| --- | -------------------------------- | ------- | ---- | ------------------------------------------------------------ |
|
||||
| 1 | Schema Pipeline & RJSF Setup | ✅ Done | — | Install deps, create schema transformer, set up RJSF theme |
|
||||
| 2 | Core Reusable Section Components | ✅ Done | — | Create shared components for detect, record, snapshots, etc. |
|
||||
| 3 | Global Configuration View | ✅ Done | — | Build main config sections (mqtt, auth, database, etc.) |
|
||||
| 4 | Camera Configuration with Tabs | ✅ Done | — | Multi-camera tabs, override indicators, section reuse |
|
||||
| 5 | Advanced Settings & Exclusions | ✅ Done | — | Progressive disclosure, contributor-editable lists |
|
||||
| 6 | Validation & Error Handling | ✅ Done | — | Inline errors, save blocking, API integration |
|
||||
| 7 | Integration & Polish | ✅ Done | — | Settings page integration, translations, documentation |
|
||||
|
||||
**Status:** ⬜ Not Started → 📋 Planned → 🔄 In Progress → ✅ Done
|
||||
|
||||
---
|
||||
|
||||
## Phase Details
|
||||
|
||||
### Phase 1: Schema Pipeline & RJSF Setup
|
||||
|
||||
**Overview**: Install react-jsonschema-form dependencies, create a schema transformation layer to convert Pydantic JSON Schema to RJSF-compatible format with UI customizations.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. **Install Dependencies**
|
||||
- Add to package.json: @rjsf/core, @rjsf/utils, @rjsf/validator-ajv8
|
||||
|
||||
2. **Create Schema Transformer**
|
||||
- File: `web/src/lib/config-schema/`
|
||||
- Transform Pydantic schema to RJSF uiSchema
|
||||
- Handle nested $defs/references
|
||||
- Apply field ordering
|
||||
|
||||
3. **Create Custom RJSF Theme**
|
||||
- File: `web/src/components/config-form/theme/`
|
||||
- Map RJSF templates to existing shadcn/ui components
|
||||
- Custom widgets for special types (color, coordinates, etc.)
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- RJSF renders basic form from schema
|
||||
- Existing UI component styling preserved
|
||||
- Schema fetching from /api/config/schema.json works
|
||||
|
||||
### Phase 2: Core Reusable Section Components
|
||||
|
||||
**Overview**: Create composable section components for config areas that appear at both global and camera levels.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. **Section Component Architecture**
|
||||
- File: `web/src/components/config-form/sections/`
|
||||
- Create: DetectSection, RecordSection, SnapshotsSection, MotionSection, ObjectsSection, ReviewSection, AudioSection, NotificationsSection, LiveSection, TimestampSection
|
||||
|
||||
2. **Each Section Component**:
|
||||
- Accepts `level: "global" | "camera"` prop
|
||||
- Accepts `cameraName?: string` for camera context
|
||||
- Accepts `showOverrideIndicator?: boolean`
|
||||
- Uses shared RJSF form with section-specific uiSchema
|
||||
|
||||
3. **Override Detection Hook**
|
||||
- File: `web/src/hooks/use-config-override.ts`
|
||||
- Compare camera value vs global default
|
||||
- Return override status for visual indicators
|
||||
|
||||
4. **Field Ordering and Layout Customization**
|
||||
|
||||
**Requirement**: Field ordering and layout within each section must be easily customizable by contributors without requiring deep knowledge of RJSF internals.
|
||||
|
||||
**Implementation Approach**:
|
||||
|
||||
- Each reusable section component (DetectSection, RecordSection, etc.) should define its own field ordering and layout configuration
|
||||
- This can be accomplished as a TypeScript constant within the section component file itself
|
||||
- The configuration should specify:
|
||||
- Field display order
|
||||
- Field grouping (which fields appear together)
|
||||
- Layout hints (e.g., multiple fields per row, nested groupings)
|
||||
- Any section-specific uiSchema customizations
|
||||
|
||||
**Example Structure**:
|
||||
|
||||
```typescript
|
||||
// In DetectSection.tsx or DetectSection.config.ts
|
||||
export const detectSectionConfig = {
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"fps",
|
||||
"width",
|
||||
"height",
|
||||
"max_disappeared",
|
||||
"stationary",
|
||||
],
|
||||
fieldGroups: {
|
||||
resolution: ["width", "height"],
|
||||
performance: ["fps", "max_disappeared"],
|
||||
},
|
||||
// ... other layout hints
|
||||
};
|
||||
```
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- Contributors can reorder fields by editing a clear configuration structure
|
||||
- No need to modify RJSF internals or complex uiSchema objects directly
|
||||
- Layout changes are localized to single files per section
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- DetectSection renders identically at global and camera level
|
||||
- Override indicators show when camera differs from global
|
||||
- Adding new fields requires editing only section definition
|
||||
|
||||
### Phase 3: Global Configuration View
|
||||
|
||||
**Overview**: Build the global configuration form for non-camera settings.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. **Global Config View**
|
||||
- File: `web/src/views/settings/GlobalConfigView.tsx`
|
||||
- Sections: MQTT, Auth, Database, Telemetry, TLS, Proxy, Networking, UI, Detectors, Model, GenAI, Classification, Birdseye
|
||||
|
||||
2. **Per-Section Subforms**
|
||||
- Each section as collapsible card
|
||||
- Progressive disclosure for advanced fields
|
||||
- Individual save buttons per section OR unified save
|
||||
|
||||
3. **Translations**
|
||||
- File: `web/public/locales/en/views/settings.json`
|
||||
- Add keys for all config field labels/descriptions
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- All global-only settings configurable
|
||||
- Proper field grouping and labels
|
||||
- Validation errors inline
|
||||
|
||||
### Phase 4: Camera Configuration with Tabs
|
||||
|
||||
**Overview**: Create per-camera configuration with tab navigation and override indicators.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. **Camera Config View**
|
||||
- File: `web/src/views/settings/CameraConfigView.tsx`
|
||||
- Tab per camera
|
||||
- Uses reusable section components
|
||||
|
||||
2. **Override Visual Indicators**
|
||||
- Badge/icon when field overrides global
|
||||
- "Reset to global default" action
|
||||
- Color coding (e.g., highlighted border)
|
||||
|
||||
3. **Camera-Specific Sections**
|
||||
- FFmpeg inputs configuration
|
||||
- Masks and Zones (link to existing editor)
|
||||
- ONVIF settings
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- Switch between cameras via tabs
|
||||
- Clear visual distinction for overridden settings
|
||||
- Reset to global default works
|
||||
|
||||
### Phase 5: Advanced Settings & Exclusions
|
||||
|
||||
**Overview**: Implement progressive disclosure and maintainable exclusion lists.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. **Exclusion System**
|
||||
- Simple consts in the component for field names
|
||||
- Filter schema before rendering
|
||||
- Document exclusion format for contributors
|
||||
|
||||
2. **Advanced Fields Toggle**
|
||||
- "Show Advanced Settings" switch per section
|
||||
- Simple consts in the component for advanced field names
|
||||
- Default collapsed state
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- Excluded fields never shown in UI
|
||||
- Advanced fields hidden by default
|
||||
|
||||
### Phase 6: Validation & Error Handling
|
||||
|
||||
**Overview**: Ensure robust validation and user-friendly error messages.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. **Client-Side Validation**
|
||||
- ajv8 validator with Pydantic schema
|
||||
- Custom error messages for common issues
|
||||
- Real-time validation on blur
|
||||
|
||||
2. **Server-Side Validation**
|
||||
- Handle 400 responses from /config/set
|
||||
- Parse Pydantic validation errors
|
||||
- Map to form fields
|
||||
|
||||
3. **Save Blocking**
|
||||
- Disable save button when invalid
|
||||
- Show error count badge
|
||||
- Scroll to first error on submit attempt
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- Invalid forms cannot be saved
|
||||
- Errors shown inline next to fields
|
||||
- Clear error messages (not technical schema errors)
|
||||
|
||||
### Phase 7: Integration & Polish
|
||||
|
||||
**Overview**: Integrate into settings page, finalize translations, and document.
|
||||
|
||||
**Changes Required**:
|
||||
|
||||
1. **Settings Page Integration**
|
||||
- Add new views to settingsGroups in Settings.tsx
|
||||
- Sidebar navigation updates
|
||||
- Route configuration
|
||||
|
||||
2. **Translations**
|
||||
- All field labels and descriptions
|
||||
- Error messages
|
||||
- Section headers
|
||||
|
||||
3. **Documentation**
|
||||
- User-facing docs for UI configuration
|
||||
|
||||
4. **Testing**
|
||||
- Basic render tests for form components
|
||||
- Validation behavior tests
|
||||
- Save/cancel flow tests
|
||||
|
||||
**Success Criteria**:
|
||||
|
||||
- Seamless navigation from settings page
|
||||
- All strings translated
|
||||
- Documentation complete
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Project Maturity Level
|
||||
|
||||
Active Development - Frigate has extensive test infrastructure but limited frontend tests.
|
||||
|
||||
### Unit Tests
|
||||
|
||||
- Schema transformer functions
|
||||
- Override detection hook
|
||||
- Custom widgets
|
||||
- Coverage target: 70% for new components
|
||||
|
||||
### Integration/Manual Tests
|
||||
|
||||
- Full form render with live schema
|
||||
- Save/validation flow end-to-end
|
||||
- Camera tab switching
|
||||
- Override indicator accuracy
|
||||
- Mobile responsiveness
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
1. All changes are additive - existing ConfigEditor remains functional
|
||||
2. New views can be feature-flagged if needed
|
||||
3. No database migrations required
|
||||
4. No backend changes required (uses existing API)
|
||||
|
||||
---
|
||||
|
||||
## Component Hierarchy
|
||||
|
||||
```
|
||||
web/src/
|
||||
├── components/
|
||||
│ └── config-form/
|
||||
│ ├── theme/
|
||||
│ │ ├── index.ts # RJSF theme export
|
||||
│ │ ├── templates/ # Base templates (ObjectFieldTemplate, etc.)
|
||||
│ │ └── widgets/ # Custom widgets (ColorWidget, TagsWidget, etc.)
|
||||
│ ├── sections/
|
||||
│ │ ├── DetectSection.tsx # Reusable for global + camera
|
||||
│ │ ├── RecordSection.tsx
|
||||
│ │ ├── SnapshotsSection.tsx
|
||||
│ │ ├── MotionSection.tsx
|
||||
│ │ ├── ObjectsSection.tsx
|
||||
│ │ ├── ReviewSection.tsx
|
||||
│ │ ├── AudioSection.tsx
|
||||
│ │ ├── NotificationsSection.tsx
|
||||
│ │ ├── LiveSection.tsx
|
||||
│ │ └── TimestampSection.tsx
|
||||
│ └── ConfigForm.tsx # Main form wrapper
|
||||
├── lib/
|
||||
│ └── config-schema/
|
||||
│ ├── index.ts # Schema utilities
|
||||
│ ├── transformer.ts # Pydantic -> RJSF schema
|
||||
├── hooks/
|
||||
│ └── use-config-override.ts # Override detection
|
||||
└── views/
|
||||
└── settings/
|
||||
├── GlobalConfigView.tsx # Global settings form
|
||||
└── CameraConfigView.tsx # Per-camera tabs form
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
1. **RJSF over custom forms**: Leverage schema-driven forms for maintainability and automatic updates when Pydantic models change.
|
||||
|
||||
2. **Reusable sections via composition**: Same component renders at global and camera level, with props controlling context and override indicators.
|
||||
|
||||
3. **Existing UI primitives**: Custom RJSF theme wraps existing shadcn/ui components for visual consistency.
|
||||
|
||||
4. **Incremental adoption**: Existing settings views remain, new RJSF views added alongside.
|
||||
Loading…
Reference in New Issue
Block a user