Files
frigate/AGENTS.md
Josh HawkinsandGitHub caa6edecac Migrate web to ESLint 10 flat config (#24326)
* migrate web to eslint 10 flat config

ESLint 10 dropped `.eslintrc` support, so `.eslintrc.cjs` is replaced with `eslint.config.js` and the lint scripts no longer pass `--ext` or `--ignore-path`. typescript-eslint moves to 8, react-hooks to 7, and react-refresh to 0.5, and the unused jest and vitest-globals plugins are removed. Lint behaves as it did before: catch variables aren't checked, unused disable directives aren't reported, and rules newly added to the recommended sets are off until the code passes them. typescript-eslint 8 flags constants used only in `typeof`, so those are now exported, or replaced with a union type where the export would trip react-refresh.

* fix lint findings from the eslint 10 recommended rules

Remove the rule overrides from the flat config migration and fix what they were hiding. Unused catch bindings are dropped, 20 disable directives that suppressed nothing are removed (react-hooks 5.2 and 7.1.1 report identical exhaustive-deps findings with inline config ignored), dead initial values are dropped, short-circuit calls become if statements or optional calls, rethrown errors pass `cause`, and the disabled "No recordings" tooltip in `ReviewTimeline` is removed along with its memo and the `getRecordingAvailability` prop. The 3 react-refresh warnings for files that export contexts or classes are left for a later refactor.
2026-09-14 07:53:45 -06:00

13 KiB

Agent Instructions for Frigate NVR

This document provides coding guidelines and best practices for contributing to Frigate NVR, a complete and local NVR designed for Home Assistant with AI object detection.

Project Overview

Frigate NVR is a realtime object detection system for IP cameras that uses:

  • Backend: Python 3.13+ with FastAPI, OpenCV, TensorFlow/ONNX
  • Frontend: React with TypeScript, Vite, TailwindCSS
  • Architecture: Multiprocessing design with ZMQ and MQTT communication
  • Focus: Minimal resource usage with maximum performance

Code Review Guidelines

When reviewing code, do NOT comment on:

  • Missing imports - Static analysis tooling catches these
  • Code formatting - Ruff (Python) and Prettier (TypeScript/React) handle formatting
  • Minor style inconsistencies already enforced by linters

Python Backend Standards

Python Requirements

  • Compatibility: Python 3.13+
  • Language Features: Use modern Python features:
    • Pattern matching
    • Type hints (comprehensive typing preferred)
    • f-strings (preferred over % or .format())
    • Dataclasses
    • Async/await patterns

Code Quality Standards

  • Formatting: Ruff (configured in pyproject.toml)
  • Linting: Ruff with rules defined in project config
  • Type Checking: Use type hints consistently
  • Testing: unittest framework - use python3 -u -m unittest to run tests
  • Language: American English for all code, comments, and documentation
  • Punctuation: Do not use em dashes in documentation, comments, or strings; reword with standard punctuation (commas, colons, parentheses, or separate sentences)

Logging Standards

  • Logger Pattern: Use module-level logger

    import logging
    
    logger = logging.getLogger(__name__)
    
  • Format Guidelines:

    • No periods at end of log messages
    • No sensitive data (keys, tokens, passwords)
    • Use lazy logging: logger.debug("Message with %s", variable)
  • Log Levels:

    • debug: Development and troubleshooting information
    • info: Important runtime events (startup, shutdown, state changes)
    • warning: Recoverable issues that should be addressed
    • error: Errors that affect functionality but don't crash the app
    • exception: Use in except blocks to include traceback

Error Handling

  • Exception Types: Choose most specific exception available

  • Try/Catch Best Practices:

    • Only wrap code that can throw exceptions
    • Keep try blocks minimal - process data after the try/except
    • Avoid bare exceptions except in background tasks

    Bad pattern:

    try:
        data = await device.get_data()  # Can throw
        # ❌ Don't process data inside try block
        processed = data.get("value", 0) * 100
        result = processed
    except DeviceError:
        logger.error("Failed to get data")
    

    Good pattern:

    try:
        data = await device.get_data()  # Can throw
    except DeviceError:
        logger.error("Failed to get data")
        return
    
    # ✅ Process data outside try block
    processed = data.get("value", 0) * 100
    result = processed
    

Async Programming

  • External I/O: All external I/O operations must be async
  • Best Practices:
    • Avoid sleeping in loops - use asyncio.sleep() not time.sleep()
    • Avoid awaiting in loops - use asyncio.gather() instead
    • No blocking calls in async functions
    • Use asyncio.create_task() for background operations
  • Thread Safety: Use proper synchronization for shared state

Documentation Standards

  • Module Docstrings: Concise descriptions at top of files

    """Utilities for motion detection and analysis."""
    
  • Function Docstrings: Required for public functions and methods

    async def process_frame(frame: ndarray, config: Config) -> Detection:
        """Process a video frame for object detection.
    
        Args:
            frame: The video frame as numpy array
            config: Detection configuration
    
        Returns:
            Detection results with bounding boxes
        """
    
  • Comment Style:

    • Explain the "why" not just the "what"
    • Keep lines under 88 characters when possible
    • Use clear, descriptive comments

File Organization

  • API Endpoints: frigate/api/ - FastAPI route handlers
  • Configuration: frigate/config/ - Configuration parsing and validation
  • Detectors: frigate/detectors/ - Object detection backends
  • Events: frigate/events/ - Event management and storage
  • Utilities: frigate/util/ - Shared utility functions

Frontend (React/TypeScript) Standards

Internationalization (i18n)

  • CRITICAL: Never write user-facing strings directly in components

  • Always use react-i18next: Import and use the t() function

    import { useTranslation } from "react-i18next";
    
    function MyComponent() {
      const { t } = useTranslation(["views/live"]);
      return <div>{t("camera_not_found")}</div>;
    }
    
  • Translation Files: Add English strings to the appropriate json files in web/public/locales/en

  • Namespaces: Organize translations by feature/view (e.g., views/live, common, views/system)

Code Quality

  • Linting: ESLint (see web/eslint.config.js)
  • Formatting: Prettier with Tailwind CSS plugin
  • Type Safety: TypeScript strict mode enabled

Component Patterns

  • UI Components: Use Radix UI primitives (in web/src/components/ui/)
  • Styling: TailwindCSS with cn() utility for class merging
  • State Management: React hooks (useState, useEffect, useCallback, useMemo)
  • Data Fetching: Custom hooks with proper loading and error states

ESLint Rules

Key rules enforced:

  • react-hooks/rules-of-hooks: error
  • react-hooks/exhaustive-deps: error
  • no-console: error (use proper logging or remove)
  • @typescript-eslint/no-explicit-any: warn (always use proper types instead of any)
  • Unused variables must be prefixed with _
  • Comma dangles required for multiline objects/arrays

File Organization

  • Pages: web/src/pages/ - Route components
  • Views: web/src/views/ - Complex view components
  • Components: web/src/components/ - Reusable components
  • Hooks: web/src/hooks/ - Custom React hooks
  • API: web/src/api/ - API client functions
  • Types: web/src/types/ - TypeScript type definitions

Testing Requirements

Backend Testing

  • Framework: Python unittest
  • Run Command: python3 -u -m unittest
  • Location: frigate/test/
  • Coverage: Aim for comprehensive test coverage of core functionality
  • Pattern: Use TestCase classes with descriptive test method names
    class TestMotionDetection(unittest.TestCase):
        def test_detects_motion_above_threshold(self):
            # Test implementation
    

Test Best Practices

  • Always have a way to test your work and confirm your changes
  • Write tests for bug fixes to prevent regressions
  • Test edge cases and error conditions
  • Mock external dependencies (cameras, APIs, hardware)
  • Use fixtures for test data

Development Commands

Python Backend

# Run all tests
python3 -u -m unittest

# Run specific test file
python3 -u -m unittest frigate.test.test_ffmpeg_presets

# Check formatting (Ruff)
ruff format --check frigate/

# Apply formatting
ruff format frigate/

# Run linter
ruff check frigate/

# Type check
python3 -u -m mypy --config-file frigate/mypy.ini frigate

# Regenerate the OpenAPI spec after adding, changing, or removing an API
# endpoint or its auth dependency — outputs docs/static/frigate-api.yaml,
# annotated with each endpoint's auth requirement (admin / any / camera /
# public). NEVER edit that file by hand. CI runs the --check variant and fails
# if it is out of date. (from repo root)
python3 generate_api_auth_spec.py
python3 generate_api_auth_spec.py --check

Frontend (from web/ directory)

# Start dev server (AI agents should never run this directly unless asked)
npm run dev

# Build for production
npm run build

# Run linter
npm run lint

# Fix linting issues
npm run lint:fix

# Format code
npm run prettier:write

# E2E: first-time setup
npm install
npx playwright install chromium

# E2E: build the app and run all tests
npm run e2e:build && npm run e2e

# E2E: interactive UI for debugging
npm run e2e:ui

# E2E: run a specific spec
npx playwright test --config e2e/playwright.config.ts e2e/specs/live.spec.ts

# E2E: filter by name, or run only desktop/mobile
npx playwright test --config e2e/playwright.config.ts --grep="severity tab"
npx playwright test --config e2e/playwright.config.ts --project=desktop

# E2E: regenerate mock data after backend model changes (from repo root)
PYTHONPATH=. python3 web/e2e/fixtures/mock-data/generate-mock-data.py

# Regenerate config translations from Pydantic models — outputs to
# web/public/locales/en/config/{global,cameras}.json. NEVER edit those
# JSON files by hand; change the Pydantic field title/description and
# re-run this script. (from repo root)
python3 generate_config_translations.py

# Extract i18n keys from source into the locale files after adding
# new t() calls. Use the :ci variant to verify the locale files are
# in sync with source (fails if extraction would change anything).
npm run i18n:extract
npm run i18n:extract:ci

Docker Development

AI agents should never run these commands directly unless instructed.

# Build local image
make local

# Build debug image
make debug

Common Patterns

API Endpoint Pattern

from fastapi import APIRouter, Request
from frigate.api.defs.tags import Tags

router = APIRouter(tags=[Tags.Events])

@router.get("/events")
async def get_events(request: Request, limit: int = 100):
    """Retrieve events from the database."""
    # Implementation

After adding, changing, or removing an endpoint (or its auth dependency), regenerate the OpenAPI spec with python3 generate_api_auth_spec.py so docs/static/frigate-api.yaml stays in sync and the endpoint's auth requirement is documented. CI enforces this via the --check variant; never edit that file by hand.

Configuration Access

# Access Frigate configuration
config: FrigateConfig = request.app.frigate_config
camera_config = config.cameras["front_door"]

Database Queries

from frigate.models import Event

# Use Peewee ORM for database access
events = (
    Event.select()
    .where(Event.camera == camera_name)
    .order_by(Event.start_time.desc())
    .limit(limit)
)

Common Anti-Patterns to Avoid

❌ Avoid These

# Blocking operations in async functions
data = requests.get(url)  # ❌ Use async HTTP client
time.sleep(5)  # ❌ Use asyncio.sleep()

# Hardcoded strings in React components
<div>Camera not found</div>  # ❌ Use t("camera_not_found")

# Missing error handling
data = await api.get_data()  # ❌ No exception handling

# Bare exceptions in regular code
try:
    value = await sensor.read()
except Exception:  # ❌ Too broad
    logger.error("Failed")

# Returning exceptions in JSON responses
except ValueError as e:
    return JSONResponse(
        content={"success": False, "message": str(e)},
    )

✅ Use These Instead

# Async operations
import aiohttp
async with aiohttp.ClientSession() as session:
    async with session.get(url) as response:
        data = await response.json()

await asyncio.sleep(5)  # ✅ Non-blocking

# Translatable strings in React
const { t } = useTranslation();
<div>{t("camera_not_found")}</div>  # ✅ Translatable

# Proper error handling
try:
    data = await api.get_data()
except ApiException as err:
    logger.error("API error: %s", err)
    raise

# Specific exceptions
try:
    value = await sensor.read()
except SensorException as err:  # ✅ Specific
    logger.exception("Failed to read sensor")

# Safe error responses
except ValueError:
    logger.exception("Invalid parameters for API request")
    return JSONResponse(
        content={
            "success": False,
            "message": "Invalid request parameters",
        },
    )

WebSocket Broadcasts

Outbound WebSocket broadcasts go through a per-recipient classifier in frigate/comms/ws.py that enforces camera-level access. The classifier is fail-closed: any topic it doesn't recognize is dropped for every client. New outbound topics must be classified there or they'll silently disappear.

Project-Specific Conventions

Configuration Files

  • Main config: config/config.yml

Directory Structure

  • Backend code: frigate/
  • Frontend code: web/
  • Docker files: docker/
  • Documentation: docs/
  • Database migrations: migrations/

Code Style Conformance

Always conform new and refactored code to the existing coding style in the project:

  • Follow established patterns in similar files
  • Match indentation and formatting of surrounding code
  • Use consistent naming conventions (snake_case for Python, camelCase for TypeScript)
  • Maintain the same level of verbosity in comments and docstrings

Additional Resources