diff --git a/docs/docs/configuration/advanced/reference.md b/docs/docs/configuration/advanced/reference.md index 6d914fe300..cf601b1dd7 100644 --- a/docs/docs/configuration/advanced/reference.md +++ b/docs/docs/configuration/advanced/reference.md @@ -252,16 +252,14 @@ birdseye: # 1 is the highest quality, and 31 is the lowest. Lower quality feeds utilize less CPU resources. quality: 8 # Optional: Activity types that include cameras in Birdseye (default: shown below) - # Multiple activity types can be enabled at the same time. - mode: - # Optional: All cameras are included always (default: shown below) - continuous: False - # Optional: Cameras are included if motion was detected in the last 30 seconds (default: shown below) - motion: False - # Optional: Cameras are included if they have had an active tracked object within the last 30 seconds (default: shown below) - objects: True - # Optional: Cameras are included while they have a stationary tracked object (default: shown below) - stationary_objects: False + # Multiple activity types can be listed at the same time. + # continuous: all cameras are included always + # motion: included if motion was detected within the inactivity threshold + # all_objects: included if a tracked object was present within the inactivity threshold + # alerts: included while an alert review item is in progress + # detections: included while a detection review item is in progress + modes: + - all_objects # Optional: Threshold for camera activity to stop showing camera (default: shown below) inactivity_threshold: 30 # Optional: Configure the birdseye layout diff --git a/docs/docs/configuration/birdseye.md b/docs/docs/configuration/birdseye.md index 689e11c2d9..1963e6ac2c 100644 --- a/docs/docs/configuration/birdseye.md +++ b/docs/docs/configuration/birdseye.md @@ -20,12 +20,15 @@ Each camera tile in Birdseye is composed from the frames of the stream assigned ### Birdseye Activity Types -Birdseye offers independent activity types that control when cameras are shown. Multiple activity types can be enabled together. +Birdseye offers independent activity types that control when cameras are shown. Multiple activity types can be listed together. - **continuous:** The camera is always included - **motion:** The camera is included when motion was detected within the last 30 seconds -- **objects:** The camera is included when an active object was tracked within the last 30 seconds -- **stationary_objects:** The camera is included while a stationary object is tracked +- **all_objects:** The camera is included when a tracked object is present, active or stationary +- **alerts:** The camera is included while an alert review item is in progress +- **detections:** The camera is included while a detection review item is in progress + +`alerts` and `detections` follow the review item's own lifetime, so the camera is removed as soon as the review item ends. Which objects qualify for each is set in [review configuration](./review.md). ### Custom Birdseye Icon @@ -50,20 +53,19 @@ To include a camera in Birdseye view only for specific circumstances, or exclude -```yaml {8-11,13-15} +```yaml {10-12,15-16} # Include all cameras by default in Birdseye view birdseye: enabled: True - mode: - continuous: True + modes: + - continuous cameras: front: - # Only include the "front" camera in Birdseye view when objects are detected + # Only include the "front" camera in Birdseye view when an alert is in progress birdseye: - mode: - continuous: False - objects: True + modes: + - alerts back: # Exclude the "back" camera from Birdseye view birdseye: @@ -75,7 +77,7 @@ cameras: ### Birdseye Inactivity -By default birdseye shows all cameras that have had the configured activity in the last 30 seconds. This threshold can be configured. +By default birdseye shows all cameras that have had the configured activity in the last 30 seconds. This threshold can be configured, and applies to the `motion` and `all_objects` activity types only. @@ -144,7 +146,8 @@ Navigate to and in the **Camera # Include all cameras by default in Birdseye view birdseye: enabled: True - mode: continuous + modes: + - continuous cameras: front: diff --git a/docs/docs/integrations/mqtt.md b/docs/docs/integrations/mqtt.md index 6f4c6cc8a1..002feafe3d 100644 --- a/docs/docs/integrations/mqtt.md +++ b/docs/docs/integrations/mqtt.md @@ -553,23 +553,25 @@ must be enabled in the configuration. Topic with current state of Birdseye for a camera. Published values are `ON` and `OFF`. -### `frigate//birdseye_mode/set` +### `frigate//birdseye_modes/set` -Topic to set the Birdseye activity types for a camera. Send one uppercase activity type or combine multiple types with commas, for example `MOTION,OBJECTS,STATIONARY_OBJECTS`. +Topic to set the Birdseye activity types for a camera. Send one uppercase activity type or combine multiple types with commas, for example `MOTION,ALERTS`. _Note: Changing the value from `CONTINUOUS` to non-continuous activity types will take up to 30 seconds for the camera to be removed from the view._ -| Command | Description | -| -------------------- | ---------------------------------------------------------------- | -| `CONTINUOUS` | Always included | -| `MOTION` | Shown if motion was detected within the last 30 seconds | -| `OBJECTS` | Shown if an active object was tracked within the last 30 seconds | -| `STATIONARY_OBJECTS` | Shown while a stationary object is tracked | +| Command | Description | +| ------------- | ---------------------------------------------------------------- | +| `CONTINUOUS` | Always included | +| `MOTION` | Shown if motion was detected within the last 30 seconds | +| `ALL_OBJECTS` | Shown if a tracked object was present within the last 30 seconds | +| `ALERTS` | Shown while an alert review item is in progress | +| `DETECTIONS` | Shown while a detection review item is in progress | +| `NONE` | Never included | -### `frigate//birdseye_mode/state` +### `frigate//birdseye_modes/state` -Topic with the current Birdseye activity types for a camera. Multiple enabled types are published as a comma-separated value in the order `OBJECTS`, `MOTION`, `STATIONARY_OBJECTS`, `CONTINUOUS`. +Topic with the current Birdseye activity types for a camera. Multiple enabled types are published as a comma-separated value in the order `CONTINUOUS`, `MOTION`, `ALL_OBJECTS`, `ALERTS`, `DETECTIONS`. `NONE` is published when no activity types are enabled. ### `frigate//notifications/set` diff --git a/docs/static/frigate-api.yaml b/docs/static/frigate-api.yaml index 3d206d677a..a78c711073 100644 --- a/docs/static/frigate-api.yaml +++ b/docs/static/frigate-api.yaml @@ -713,7 +713,7 @@ paths: | `improve_contrast` | `ON`, `OFF` | | `ptz_autotracker` | `ON`, `OFF` | | `birdseye` | `ON`, `OFF` | - | `birdseye_mode` | `CONTINUOUS`, `MOTION`, `OBJECTS` | + | `birdseye_modes` | `CONTINUOUS`, `MOTION`, `ALL_OBJECTS`, `ALERTS`, `DETECTIONS`, `NONE`, or a comma-separated combination | | `motion_contour_area` | integer | | `motion_threshold` | integer | | `motion_mask` | `ON`, `OFF` | @@ -803,7 +803,7 @@ paths: | `improve_contrast` | `ON`, `OFF` | | `ptz_autotracker` | `ON`, `OFF` | | `birdseye` | `ON`, `OFF` | - | `birdseye_mode` | `CONTINUOUS`, `MOTION`, `OBJECTS` | + | `birdseye_modes` | `CONTINUOUS`, `MOTION`, `ALL_OBJECTS`, `ALERTS`, `DETECTIONS`, `NONE`, or a comma-separated combination | | `motion_contour_area` | integer | | `motion_threshold` | integer | | `motion_mask` | `ON`, `OFF` | diff --git a/frigate/api/camera.py b/frigate/api/camera.py index 9411dc5041..f4a844164c 100644 --- a/frigate/api/camera.py +++ b/frigate/api/camera.py @@ -1386,7 +1386,7 @@ def camera_set( | `improve_contrast` | `ON`, `OFF` | | `ptz_autotracker` | `ON`, `OFF` | | `birdseye` | `ON`, `OFF` | - | `birdseye_mode` | `CONTINUOUS`, `MOTION`, `OBJECTS` | + | `birdseye_modes` | `CONTINUOUS`, `MOTION`, `ALL_OBJECTS`, `ALERTS`, `DETECTIONS`, `NONE`, or a comma-separated combination | | `motion_contour_area` | integer | | `motion_threshold` | integer | | `motion_mask` | `ON`, `OFF` | diff --git a/frigate/comms/dispatcher.py b/frigate/comms/dispatcher.py index cc208a9c9d..1aedaa6e61 100644 --- a/frigate/comms/dispatcher.py +++ b/frigate/comms/dispatcher.py @@ -11,7 +11,11 @@ from frigate.camera.activity_manager import AudioActivityManager, CameraActivity from frigate.comms.base_communicator import Communicator from frigate.comms.runtime_state import RuntimeStatePersistence from frigate.comms.webpush import WebPushClient -from frigate.config import BirdseyeModeConfig, FrigateConfig +from frigate.config import ( + FrigateConfig, + birdseye_modes_from_mqtt_payload, + birdseye_modes_to_mqtt_payload, +) from frigate.config.camera.updater import ( CameraConfigUpdateEnum, CameraConfigUpdatePublisher, @@ -84,7 +88,7 @@ class Dispatcher: "recordings": self._on_recordings_command, "snapshots": self._on_snapshots_command, "birdseye": self._on_birdseye_command, - "birdseye_mode": self._on_birdseye_mode_command, + "birdseye_modes": self._on_birdseye_modes_command, "review_alerts": self._on_alerts_command, "review_detections": self._on_detections_command, "object_descriptions": self._on_object_description_command, @@ -879,12 +883,12 @@ class Dispatcher: ) self.publish(f"{camera_name}/birdseye/state", payload, retain=True) - def _on_birdseye_mode_command(self, camera_name: str, payload: str) -> None: + def _on_birdseye_modes_command(self, camera_name: str, payload: str) -> None: """Callback for birdseye mode topic.""" - mode = BirdseyeModeConfig.from_mqtt_payload(payload) - if mode is None: - logger.info("Invalid birdseye_mode command: %s", payload) + modes = birdseye_modes_from_mqtt_payload(payload) + if modes is None: + logger.info("Invalid birdseye_modes command: %s", payload) return birdseye_settings = self.config.cameras[camera_name].birdseye @@ -893,9 +897,9 @@ class Dispatcher: logger.info(f"Birdseye mode not enabled for {camera_name}") return - birdseye_settings.mode = mode + birdseye_settings.modes = modes logger.info( - f"Setting birdseye mode for {camera_name} to {birdseye_settings.mode}" + f"Setting birdseye mode for {camera_name} to {birdseye_settings.modes}" ) self.config_updater.publish_update( @@ -903,7 +907,9 @@ class Dispatcher: birdseye_settings, ) self.publish( - f"{camera_name}/birdseye_mode/state", mode.to_mqtt_payload(), retain=True + f"{camera_name}/birdseye_modes/state", + birdseye_modes_to_mqtt_payload(modes), + retain=True, ) def _on_camera_notification_command(self, camera_name: str, payload: str) -> None: diff --git a/frigate/comms/mqtt.py b/frigate/comms/mqtt.py index 453a53ff51..c7ef3e6441 100644 --- a/frigate/comms/mqtt.py +++ b/frigate/comms/mqtt.py @@ -7,7 +7,7 @@ import paho.mqtt.client as mqtt from paho.mqtt.enums import CallbackAPIVersion from frigate.comms.base_communicator import Communicator -from frigate.config import FrigateConfig +from frigate.config import FrigateConfig, birdseye_modes_to_mqtt_payload logger = logging.getLogger(__name__) @@ -123,9 +123,9 @@ class MqttClient(Communicator): retain=True, ) self.publish( - f"{camera_name}/birdseye_mode/state", + f"{camera_name}/birdseye_modes/state", ( - camera.birdseye.mode.to_mqtt_payload() + birdseye_modes_to_mqtt_payload(camera.birdseye.modes) if camera.birdseye.enabled else "OFF" ), @@ -270,7 +270,7 @@ class MqttClient(Communicator): "motion_threshold", "motion_contour_area", "birdseye", - "birdseye_mode", + "birdseye_modes", "review_alerts", "review_detections", "object_descriptions", diff --git a/frigate/config/camera/birdseye.py b/frigate/config/camera/birdseye.py index eefe6886d9..c62befb4dc 100644 --- a/frigate/config/camera/birdseye.py +++ b/frigate/config/camera/birdseye.py @@ -1,3 +1,5 @@ +from enum import Enum + from pydantic import BaseModel, Field from ..base import FrigateBaseModel @@ -6,78 +8,58 @@ __all__ = [ "BirdseyeCameraConfig", "BirdseyeConfig", "BirdseyeLayoutConfig", - "BirdseyeModeConfig", + "BirdseyeModeEnum", + "birdseye_modes_from_mqtt_payload", + "birdseye_modes_to_mqtt_payload", ] -BIRDSEYE_ACTIVITY_TYPES = ( - "objects", - "motion", - "stationary_objects", - "continuous", -) +# canonical MQTT payload for an empty mode list +MQTT_NO_MODES = "NONE" -class BirdseyeModeConfig(FrigateBaseModel): - continuous: bool = Field( - default=False, - title="Continuous", - description="Always include the camera in Birdseye.", - ) - motion: bool = Field( - default=False, - title="Motion", - description="Include the camera in Birdseye when motion is detected.", - ) - objects: bool = Field( - default=False, - title="Active objects", - description="Include the camera in Birdseye while an active object is tracked.", - ) - stationary_objects: bool = Field( - default=False, - title="Stationary objects", - description="Include the camera in Birdseye while a stationary object is tracked.", - ) +class BirdseyeModeEnum(str, Enum): + continuous = "continuous" + motion = "motion" + all_objects = "all_objects" + alerts = "alerts" + detections = "detections" - @classmethod - def from_mqtt_payload(cls, payload: str) -> "BirdseyeModeConfig | None": - """Create mode options from an uppercase MQTT payload.""" - raw_modes = payload.split(",") - if not raw_modes or any(not mode for mode in raw_modes): + +def birdseye_modes_from_mqtt_payload(payload: str) -> list[BirdseyeModeEnum] | None: + """Parse an uppercase MQTT payload into activity modes, or None when invalid.""" + raw_modes = payload.split(",") + + if any(not raw_mode or raw_mode != raw_mode.upper() for raw_mode in raw_modes): + return None + + if raw_modes == [MQTT_NO_MODES]: + return [] + + modes: list[BirdseyeModeEnum] = [] + + for raw_mode in raw_modes: + try: + mode = BirdseyeModeEnum(raw_mode.lower()) + except ValueError: return None - modes = [mode.lower() for mode in raw_modes] - if any( - raw_mode != mode.upper() or mode not in BIRDSEYE_ACTIVITY_TYPES - for raw_mode, mode in zip(raw_modes, modes) - ): + if mode in modes: return None - if len(modes) != len(set(modes)): - return None + modes.append(mode) - return cls(**{mode: True for mode in modes}) - - def has_enabled_activity(self) -> bool: - """Return whether at least one activity type is enabled.""" - return any(getattr(self, activity) for activity in BIRDSEYE_ACTIVITY_TYPES) - - def to_mqtt_payload(self) -> str: - """Serialize enabled mode options for MQTT state topics.""" - payload = ",".join( - activity.upper() - for activity in BIRDSEYE_ACTIVITY_TYPES - if getattr(self, activity) - ) - if not payload: - raise ValueError("At least one Birdseye activity type must be enabled") - - return payload + return modes -def default_birdseye_mode() -> BirdseyeModeConfig: - """Return the default Birdseye mode configuration.""" - return BirdseyeModeConfig(objects=True) +def birdseye_modes_to_mqtt_payload(modes: list[BirdseyeModeEnum]) -> str: + """Serialize activity modes for MQTT state topics.""" + payload = ",".join(mode.value.upper() for mode in BirdseyeModeEnum if mode in modes) + return payload or MQTT_NO_MODES + + +def default_birdseye_modes() -> list[BirdseyeModeEnum]: + """Return the default Birdseye activity modes.""" + return [BirdseyeModeEnum.all_objects] class BirdseyeLayoutConfig(FrigateBaseModel): @@ -101,8 +83,8 @@ class BirdseyeConfig(FrigateBaseModel): title="Enable Birdseye", description="Enable or disable the Birdseye view feature.", ) - mode: BirdseyeModeConfig = Field( - default_factory=default_birdseye_mode, + modes: list[BirdseyeModeEnum] = Field( + default_factory=default_birdseye_modes, title="Activity types", description="Activity types that include cameras in Birdseye.", ) @@ -156,8 +138,8 @@ class BirdseyeCameraConfig(BaseModel): title="Enable Birdseye", description="Enable or disable the Birdseye view feature.", ) - mode: BirdseyeModeConfig = Field( - default_factory=default_birdseye_mode, + modes: list[BirdseyeModeEnum] = Field( + default_factory=default_birdseye_modes, title="Activity types", description="Activity types that include cameras in Birdseye.", ) diff --git a/frigate/config/config.py b/frigate/config/config.py index 78611eac74..1b32d64747 100644 --- a/frigate/config/config.py +++ b/frigate/config/config.py @@ -41,7 +41,7 @@ from .auth import AuthConfig from .base import FrigateBaseModel from .camera import CameraConfig, CameraLiveConfig from .camera.audio import AudioConfig, AudioFilterConfig -from .camera.birdseye import BirdseyeConfig, BirdseyeModeConfig +from .camera.birdseye import BirdseyeConfig from .camera.detect import DetectConfig from .camera.ffmpeg import FfmpegConfig from .camera.genai import GenAIConfig, GenAIRoleEnum @@ -349,20 +349,8 @@ def verify_required_zones_exist(camera_config: CameraConfig) -> None: def verify_profile_overrides_match_base(camera_config: CameraConfig) -> None: - """Verify profile overrides against the resolved base camera configuration.""" + """Verify that profile zone and mask IDs reference entries defined on the base camera.""" for profile_name, profile in camera_config.profiles.items(): - if profile.birdseye is not None: - overrides = profile.birdseye.mode.model_dump(exclude_unset=True) - base_mode = camera_config.birdseye.mode.model_dump() - resolved_mode = BirdseyeModeConfig.model_validate( - deep_merge(overrides, base_mode) - ) - if not resolved_mode.has_enabled_activity(): - raise ValueError( - f"Camera '{camera_config.name}' profile '{profile_name}' must " - "enable at least one Birdseye activity type" - ) - if profile.zones: for zone_name in profile.zones: if zone_name not in camera_config.zones: @@ -1033,10 +1021,6 @@ class FrigateConfig(FrigateBaseModel): self.cameras[name] = camera_config verify_config_roles(camera_config) - if not camera_config.birdseye.mode.has_enabled_activity(): - raise ValueError( - f"Camera '{name}' must enable at least one Birdseye activity type" - ) verify_valid_live_stream_names(self, camera_config) verify_recording_segments_setup_with_reasonable_time(camera_config) verify_zone_objects_are_tracked(camera_config) diff --git a/frigate/config/profile_manager.py b/frigate/config/profile_manager.py index ab3cf94e00..b7421ab325 100644 --- a/frigate/config/profile_manager.py +++ b/frigate/config/profile_manager.py @@ -8,6 +8,7 @@ from datetime import UTC, datetime from pathlib import Path from typing import Any +from frigate.config.camera.birdseye import birdseye_modes_to_mqtt_payload from frigate.config.camera.updater import ( CameraConfigUpdateEnum, CameraConfigUpdatePublisher, @@ -42,9 +43,11 @@ SECTION_STATE_TOPICS: dict[str, list[tuple[str, Callable[[Any], Any]]]] = { "birdseye": [ ("birdseye", lambda c: "ON" if c.birdseye.enabled else "OFF"), ( - "birdseye_mode", + "birdseye_modes", lambda c: ( - c.birdseye.mode.to_mqtt_payload() if c.birdseye.enabled else "OFF" + birdseye_modes_to_mqtt_payload(c.birdseye.modes) + if c.birdseye.enabled + else "OFF" ), ), ], diff --git a/frigate/genai/prompts.py b/frigate/genai/prompts.py index 48fb8320c2..973ac89b79 100644 --- a/frigate/genai/prompts.py +++ b/frigate/genai/prompts.py @@ -453,7 +453,7 @@ def get_tool_definitions( "motion", "enabled", "birdseye", - "birdseye_mode", + "birdseye_modes", "improve_contrast", "ptz_autotracker", "motion_contour_area", @@ -468,7 +468,7 @@ def get_tool_definitions( ], "description": ( "The feature to change. Most features accept ON or OFF. " - "birdseye_mode accepts CONTINUOUS, MOTION, OBJECTS, STATIONARY_OBJECTS, or a comma-separated combination. " + "birdseye_modes accepts CONTINUOUS, MOTION, ALL_OBJECTS, ALERTS, DETECTIONS, NONE, or a comma-separated combination. " "motion_contour_area and motion_threshold accept a number. " "profile accepts a profile name or 'none' to deactivate (requires camera='*')." ), diff --git a/frigate/output/birdseye.py b/frigate/output/birdseye.py index 72d60f163b..877f5133ca 100644 --- a/frigate/output/birdseye.py +++ b/frigate/output/birdseye.py @@ -17,9 +17,11 @@ import cv2 import numpy as np from frigate.comms.inter_process import InterProcessRequestor -from frigate.config import BirdseyeModeConfig, FfmpegConfig, FrigateConfig +from frigate.comms.review_updater import ReviewDataSubscriber +from frigate.config import BirdseyeModeEnum, FfmpegConfig, FrigateConfig from frigate.const import BASE_DIR, BIRDSEYE_PIPE, INSTALL_DIR, UPDATE_BIRDSEYE_LAYOUT from frigate.output.ws_auth import ws_has_camera_access +from frigate.review.types import SeverityEnum from frigate.util.image import ( SharedMemoryFrameManager, copy_yuv_to_position, @@ -33,9 +35,9 @@ logger = logging.getLogger(__name__) class BirdseyeActivity: """Activity signals used to decide whether a camera is shown in Birdseye.""" - has_active_object: bool - has_stationary_object: bool + has_object: bool has_motion: bool + severity: str | None def get_standard_aspect_ratio(width: int, height: int) -> tuple[int, int]: @@ -367,6 +369,7 @@ class BirdsEyeFrameManager: settings.detect.height, ], "last_active_frame": 0.0, + "live_active": False, "current_frame": 0.0, "layout_frame": 0.0, "channel_dims": { @@ -418,16 +421,32 @@ class BirdsEyeFrameManager: channel_dims, ) - def camera_active( + def camera_threshold_active( self, - mode: BirdseyeModeConfig, + modes: list[BirdseyeModeEnum], activity: BirdseyeActivity, ) -> bool: + """Return whether activity subject to inactivity_threshold is present.""" + return (BirdseyeModeEnum.motion in modes and activity.has_motion) or ( + BirdseyeModeEnum.all_objects in modes and activity.has_object + ) + + def camera_live_active( + self, + modes: list[BirdseyeModeEnum], + activity: BirdseyeActivity, + ) -> bool: + """Return whether activity that ends the moment it stops is present.""" return ( - mode.continuous - or (mode.motion and activity.has_motion) - or (mode.objects and activity.has_active_object) - or (mode.stationary_objects and activity.has_stationary_object) + BirdseyeModeEnum.continuous in modes + or ( + BirdseyeModeEnum.alerts in modes + and activity.severity == SeverityEnum.alert + ) + or ( + BirdseyeModeEnum.detections in modes + and activity.severity == SeverityEnum.detection + ) ) def get_camera_coordinates(self) -> dict[str, dict[str, int]]: @@ -459,9 +478,15 @@ class BirdsEyeFrameManager: and self.config.cameras[cam].birdseye.enabled and self.config.cameras[cam].enabled_in_config and self.config.cameras[cam].enabled - and cam_data["last_active_frame"] > 0 - and cam_data["current_frame_time"] - cam_data["last_active_frame"] - < self.config.birdseye.inactivity_threshold + and ( + cam_data["live_active"] + or ( + cam_data["last_active_frame"] > 0 + and cam_data["current_frame_time"] + - cam_data["last_active_frame"] + < self.config.birdseye.inactivity_threshold + ) + ) ] ) logger.debug(f"Active cameras: {active_cameras}") @@ -478,7 +503,9 @@ class BirdsEyeFrameManager: limited_active_cameras = sorted( active_cameras, key=lambda active_camera: ( - self.cameras[active_camera]["current_frame_time"] + 0.0 + if self.cameras[active_camera]["live_active"] + else self.cameras[active_camera]["current_frame_time"] - self.cameras[active_camera]["last_active_frame"] ), ) @@ -754,10 +781,10 @@ class BirdsEyeFrameManager: # disabling birdseye is a little tricky if not camera_config.birdseye.enabled or not camera_config.enabled: - # if we've rendered a frame (we have a value for last_active_frame) - # then we need to set it to zero - if camera_state["last_active_frame"] > 0: + # if we've rendered a frame (we have activity state) then clear it + if camera_state["last_active_frame"] > 0 or camera_state["live_active"]: camera_state["last_active_frame"] = 0 + camera_state["live_active"] = False force_update = True else: return False, False @@ -765,12 +792,13 @@ class BirdsEyeFrameManager: # update the last active frame for the camera camera_state["current_frame"] = frame.copy() camera_state["current_frame_time"] = frame_time - if self.camera_active( - camera_config.birdseye.mode, - activity, - ): + modes = camera_config.birdseye.modes + + if self.camera_threshold_active(modes, activity): camera_state["last_active_frame"] = frame_time + camera_state["live_active"] = self.camera_live_active(modes, activity) + now = datetime.datetime.now().timestamp() # limit output to 10 fps @@ -828,6 +856,8 @@ class Birdseye: self.frame_manager = SharedMemoryFrameManager() self.stop_event = stop_event self.requestor = InterProcessRequestor() + self.review_subscriber = ReviewDataSubscriber("") + self.review_severity: dict[str, str] = {} self.idle_fps: float = self.config.birdseye.idle_heartbeat_fps self._idle_interval: float | None = ( (1.0 / self.idle_fps) if self.idle_fps > 0 else None @@ -858,6 +888,21 @@ class Birdseye: self.birdseye_manager.clear_frame() self.__send_new_frame() + def check_review_updates(self) -> None: + """Drain review updates so each camera's active severity stays current.""" + while True: + update = self.review_subscriber.check_for_update(timeout=0) + + if update is None: + break + + camera = update["after"]["camera"] + + if update["type"] == "end": + self.review_severity.pop(camera, None) + else: + self.review_severity[camera] = update["after"]["severity"] + def add_camera(self, camera: str) -> None: """Add a camera to the birdseye manager.""" self.birdseye_manager.add_camera(camera) @@ -866,6 +911,7 @@ class Birdseye: def remove_camera(self, camera: str) -> None: """Remove a camera from the birdseye manager.""" self.birdseye_manager.remove_camera(camera) + self.review_severity.pop(camera, None) logger.debug(f"Removed camera {camera} from birdseye") def write_data( @@ -876,24 +922,13 @@ class Birdseye: frame_time: float, frame: np.ndarray, ) -> None: - has_active_object = False - has_stationary_object = False - for tracked_object in current_tracked_objects: - if tracked_object["stationary"]: - if not tracked_object["false_positive"]: - has_stationary_object = True - else: - # Preserve the existing objects activity behavior, which includes - # non-stationary trackers before they are confirmed. - has_active_object = True - - if has_active_object and has_stationary_object: - break - activity = BirdseyeActivity( - has_active_object=has_active_object, - has_stationary_object=has_stationary_object, + has_object=any( + not tracked_object["false_positive"] + for tracked_object in current_tracked_objects + ), has_motion=bool(motion_boxes), + severity=self.review_severity.get(camera), ) frame_changed, frame_layout_changed = self.birdseye_manager.update( @@ -919,5 +954,6 @@ class Birdseye: self.__send_new_frame() def stop(self) -> None: + self.review_subscriber.stop() self.converter.join() self.broadcaster.join() diff --git a/frigate/output/output.py b/frigate/output/output.py index 1d8b473713..69cd9eb6dc 100644 --- a/frigate/output/output.py +++ b/frigate/output/output.py @@ -188,6 +188,11 @@ class OutputProcess(FrigateProcess): self.config.birdseye = birdseye_config logger.debug("Applied dynamic birdseye config update") + # drain review updates every iteration, not just when birdseye is + # being consumed, so a dropped end never strands a camera + if birdseye is not None: + birdseye.check_review_updates() + # check if there is an updated config updates = config_subscriber.check_for_updates() diff --git a/frigate/test/http_api/test_http_config_set.py b/frigate/test/http_api/test_http_config_set.py index fc4c59e4fa..f31d382c51 100644 --- a/frigate/test/http_api/test_http_config_set.py +++ b/frigate/test/http_api/test_http_config_set.py @@ -7,7 +7,7 @@ from unittest.mock import MagicMock, Mock, patch import ruamel.yaml -from frigate.config import FrigateConfig +from frigate.config import BirdseyeModeEnum, FrigateConfig from frigate.config.camera.updater import ( CameraConfigUpdateEnum, CameraConfigUpdatePublisher, @@ -383,22 +383,15 @@ class TestConfigSetWildcardPropagation(BaseTestHttp): Global birdseye only seeds enabled and mode; the camera copies are what the output process actually reads. Sending just the global object makes a worker guess which cameras were inheriting, and the only available - guess (mode still equals the previous global) wrongly claims a camera - whose explicit yaml mode happens to match. + guess (modes still equals the previous global) wrongly claims a camera + whose explicit yaml modes happens to match. """ self.minimal_config["birdseye"] = { "enabled": True, - "mode": {"motion": True}, + "modes": ["motion"], } # explicit override that matches the global value being replaced - self.minimal_config["cameras"]["front_door"]["birdseye"] = { - "mode": { - "continuous": False, - "motion": True, - "objects": False, - "stationary_objects": False, - } - } + self.minimal_config["cameras"]["front_door"]["birdseye"] = {"modes": ["motion"]} config_path = self._write_config_file() mock_find_config.return_value = config_path @@ -409,16 +402,7 @@ class TestConfigSetWildcardPropagation(BaseTestHttp): resp = client.put( "/config/set", json={ - "config_data": { - "birdseye": { - "mode": { - "continuous": True, - "motion": False, - "objects": False, - "stationary_objects": False, - } - } - }, + "config_data": {"birdseye": {"modes": ["continuous"]}}, "update_topic": "config/birdseye", "requires_restart": 0, }, @@ -430,7 +414,7 @@ class TestConfigSetWildcardPropagation(BaseTestHttp): mock_publisher.publisher.publish.assert_called_once() topic, settings = mock_publisher.publisher.publish.call_args[0] self.assertEqual(topic, "config/birdseye") - self.assertEqual(settings.mode.to_mqtt_payload(), "CONTINUOUS") + self.assertEqual(settings.modes, [BirdseyeModeEnum.continuous]) published = { call[0][0].camera: call[0][1] @@ -445,10 +429,10 @@ class TestConfigSetWildcardPropagation(BaseTestHttp): # the override survives, the inheriting camera follows global self.assertEqual( - published["front_door"].mode.to_mqtt_payload(), "MOTION" + published["front_door"].modes, [BirdseyeModeEnum.motion] ) self.assertEqual( - published["back_yard"].mode.to_mqtt_payload(), "CONTINUOUS" + published["back_yard"].modes, [BirdseyeModeEnum.continuous] ) finally: os.unlink(config_path) diff --git a/frigate/test/test_birdseye.py b/frigate/test/test_birdseye.py index 2132296b6e..372ed5a8a1 100644 --- a/frigate/test/test_birdseye.py +++ b/frigate/test/test_birdseye.py @@ -4,7 +4,12 @@ import multiprocessing as mp import unittest from unittest.mock import Mock -from frigate.config import BirdseyeModeConfig, FrigateConfig +from frigate.config import ( + BirdseyeModeEnum, + FrigateConfig, + birdseye_modes_from_mqtt_payload, + birdseye_modes_to_mqtt_payload, +) from frigate.output.birdseye import ( Birdseye, BirdseyeActivity, @@ -215,11 +220,7 @@ class TestBirdseyeActivity(unittest.TestCase): "mqtt": {"enabled": False}, "birdseye": { "enabled": True, - "mode": { - "motion": True, - "objects": True, - "stationary_objects": True, - }, + "modes": ["motion", "all_objects"], }, "cameras": { "front": { @@ -234,53 +235,93 @@ class TestBirdseyeActivity(unittest.TestCase): } self.manager = BirdsEyeFrameManager(FrigateConfig(**config), mp.Event()) - def test_existing_modes_keep_their_activity_rules(self): - continuous = BirdseyeModeConfig(continuous=True) - motion = BirdseyeModeConfig(motion=True) - objects = BirdseyeModeConfig(objects=True) + def test_each_mode_matches_only_its_own_activity(self): + motion_activity = BirdseyeActivity( + has_object=False, has_motion=True, severity=None + ) + object_activity = BirdseyeActivity( + has_object=True, has_motion=False, severity=None + ) + no_activity = BirdseyeActivity( + has_object=False, has_motion=False, severity=None + ) - no_activity = BirdseyeActivity(False, False, False) - motion_activity = BirdseyeActivity(False, False, True) - stationary_activity = BirdseyeActivity(False, True, False) - active_object_activity = BirdseyeActivity(True, False, False) + assert self.manager.camera_threshold_active( + [BirdseyeModeEnum.motion], motion_activity + ) + assert not self.manager.camera_threshold_active( + [BirdseyeModeEnum.motion], object_activity + ) + assert self.manager.camera_threshold_active( + [BirdseyeModeEnum.all_objects], object_activity + ) + assert not self.manager.camera_threshold_active( + [BirdseyeModeEnum.all_objects], motion_activity + ) + assert self.manager.camera_live_active( + [BirdseyeModeEnum.continuous], no_activity + ) - assert self.manager.camera_active(continuous, no_activity) - assert self.manager.camera_active(motion, motion_activity) - assert not self.manager.camera_active(motion, stationary_activity) - assert self.manager.camera_active(objects, active_object_activity) - assert not self.manager.camera_active(objects, stationary_activity) + def test_continuous_is_not_threshold_activity(self): + no_activity = BirdseyeActivity( + has_object=False, has_motion=False, severity=None + ) + + assert not self.manager.camera_threshold_active( + [BirdseyeModeEnum.continuous], no_activity + ) def test_modes_can_be_combined(self): - mode = BirdseyeModeConfig(motion=True, stationary_objects=True) + modes = [BirdseyeModeEnum.motion, BirdseyeModeEnum.all_objects] - assert self.manager.camera_active(mode, BirdseyeActivity(False, False, True)) - assert self.manager.camera_active(mode, BirdseyeActivity(False, True, False)) - assert not self.manager.camera_active( - mode, BirdseyeActivity(False, False, False) + assert self.manager.camera_threshold_active( + modes, BirdseyeActivity(has_object=False, has_motion=True, severity=None) + ) + assert self.manager.camera_threshold_active( + modes, BirdseyeActivity(has_object=True, has_motion=False, severity=None) + ) + assert not self.manager.camera_threshold_active( + modes, BirdseyeActivity(has_object=False, has_motion=False, severity=None) ) - def test_stationary_objects_are_independent_from_active_objects(self): - stationary_objects = BirdseyeModeConfig(stationary_objects=True) + def test_empty_modes_never_activate(self): + activity = BirdseyeActivity(has_object=True, has_motion=True, severity=None) - assert self.manager.camera_active( - stationary_objects, BirdseyeActivity(False, True, False) + assert not self.manager.camera_threshold_active([], activity) + assert not self.manager.camera_live_active([], activity) + + def test_alerts_and_detections_match_review_severity(self): + alert = BirdseyeActivity(has_object=False, has_motion=False, severity="alert") + detection = BirdseyeActivity( + has_object=False, has_motion=False, severity="detection" ) - assert not self.manager.camera_active( - stationary_objects, BirdseyeActivity(True, False, False) + idle = BirdseyeActivity(has_object=False, has_motion=False, severity=None) + + assert self.manager.camera_live_active([BirdseyeModeEnum.alerts], alert) + assert not self.manager.camera_live_active([BirdseyeModeEnum.alerts], detection) + assert not self.manager.camera_live_active([BirdseyeModeEnum.alerts], idle) + assert self.manager.camera_live_active([BirdseyeModeEnum.detections], detection) + assert not self.manager.camera_live_active([BirdseyeModeEnum.detections], alert) + + def test_review_severity_is_not_threshold_activity(self): + alert = BirdseyeActivity(has_object=False, has_motion=False, severity="alert") + + assert not self.manager.camera_threshold_active( + [BirdseyeModeEnum.alerts, BirdseyeModeEnum.motion], alert ) - def test_write_data_preserves_active_and_confirms_stationary_activity(self): + def test_all_objects_covers_active_and_stationary_objects(self): birdseye = Birdseye.__new__(Birdseye) birdseye.birdseye_manager = Mock() birdseye.birdseye_manager.update.return_value = (False, False) birdseye._idle_interval = None + birdseye.review_severity = {} frame = Mock() birdseye.write_data( "front", [ {"stationary": True, "false_positive": True}, - {"stationary": False, "false_positive": True}, {"stationary": True, "false_positive": False}, ], [[0, 0, 10, 10]], @@ -289,26 +330,36 @@ class TestBirdseyeActivity(unittest.TestCase): ) birdseye.birdseye_manager.update.assert_called_once_with( - "front", BirdseyeActivity(True, True, True), 1.0, frame + "front", + BirdseyeActivity(has_object=True, has_motion=True, severity=None), + 1.0, + frame, ) - def test_stationary_false_positive_does_not_activate_birdseye(self): + def test_false_positives_do_not_count_as_objects(self): birdseye = Birdseye.__new__(Birdseye) birdseye.birdseye_manager = Mock() birdseye.birdseye_manager.update.return_value = (False, False) birdseye._idle_interval = None + birdseye.review_severity = {} frame = Mock() birdseye.write_data( "front", - [{"stationary": True, "false_positive": True}], + [ + {"stationary": True, "false_positive": True}, + {"stationary": False, "false_positive": True}, + ], [], 1.0, frame, ) birdseye.birdseye_manager.update.assert_called_once_with( - "front", BirdseyeActivity(False, False, False), 1.0, frame + "front", + BirdseyeActivity(has_object=False, has_motion=False, severity=None), + 1.0, + frame, ) @@ -318,7 +369,7 @@ class TestBirdseyeCameraOrder(unittest.TestCase): def setUp(self): config = { "mqtt": {"enabled": False}, - "birdseye": {"enabled": True, "mode": {"continuous": True}}, + "birdseye": {"enabled": True, "modes": ["continuous"]}, "cameras": { camera: { "ffmpeg": { @@ -340,6 +391,7 @@ class TestBirdseyeCameraOrder(unittest.TestCase): camera_data["current_frame"] = None camera_data["current_frame_time"] = 1.0 camera_data["last_active_frame"] = 1.0 + camera_data["live_active"] = True def layout_order(self) -> list[str]: """Return the cameras in the order the current layout renders them.""" @@ -377,3 +429,155 @@ class TestBirdseyeCameraOrder(unittest.TestCase): assert not layout_changed assert self.layout_order() == ["back", "front", "side"] + + +class TestBirdseyeLiveActivity(unittest.TestCase): + """Test that live activity bypasses the inactivity threshold.""" + + def setUp(self): + config = { + "mqtt": {"enabled": False}, + "birdseye": { + "enabled": True, + "modes": ["continuous"], + "inactivity_threshold": 30, + }, + "cameras": { + camera: { + "ffmpeg": { + "inputs": [ + {"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]} + ] + }, + "detect": {"height": 1080, "width": 1920, "fps": 5}, + } + for camera in ("back", "front") + }, + } + self.config = FrigateConfig(**config) + self.manager = BirdsEyeFrameManager(self.config, mp.Event()) + + for camera_data in self.manager.cameras.values(): + camera_data["current_frame"] = None + camera_data["current_frame_time"] = 1000.0 + camera_data["last_active_frame"] = 0.0 + camera_data["live_active"] = False + + def test_live_active_camera_is_shown_without_a_recent_active_frame(self): + self.manager.cameras["front"]["live_active"] = True + + self.manager.update_frame() + + assert self.manager.active_cameras == {"front"} + + def test_live_active_camera_drops_out_immediately(self): + self.manager.cameras["front"]["live_active"] = True + self.manager.update_frame() + assert self.manager.active_cameras == {"front"} + + self.manager.cameras["front"]["live_active"] = False + self.manager.update_frame() + + assert self.manager.active_cameras == set() + + def test_threshold_activity_lingers_then_expires(self): + self.manager.cameras["front"]["last_active_frame"] = 980.0 + self.manager.update_frame() + assert self.manager.active_cameras == {"front"} + + self.manager.cameras["front"]["last_active_frame"] = 960.0 + self.manager.update_frame() + + assert self.manager.active_cameras == set() + + def test_max_cameras_ranks_live_active_cameras_first(self): + self.config.birdseye.layout.max_cameras = 1 + self.manager.cameras["front"]["live_active"] = True + self.manager.cameras["back"]["last_active_frame"] = 995.0 + + self.manager.update_frame() + + assert self.manager.active_cameras == {"front"} + + +class TestBirdseyeModePayload(unittest.TestCase): + """Test the MQTT payload contract for Birdseye activity modes.""" + + def test_single_mode_round_trips(self): + modes = birdseye_modes_from_mqtt_payload("ALERTS") + + assert modes == [BirdseyeModeEnum.alerts] + assert birdseye_modes_to_mqtt_payload(modes) == "ALERTS" + + def test_combined_modes_are_published_in_enum_order(self): + modes = birdseye_modes_from_mqtt_payload("ALERTS,MOTION") + + assert modes == [BirdseyeModeEnum.alerts, BirdseyeModeEnum.motion] + assert birdseye_modes_to_mqtt_payload(modes) == "MOTION,ALERTS" + + def test_none_round_trips_to_an_empty_list(self): + assert birdseye_modes_from_mqtt_payload("NONE") == [] + assert birdseye_modes_to_mqtt_payload([]) == "NONE" + + def test_invalid_payloads_are_rejected(self): + for payload in ( + "UNKNOWN", + "motion", + "MOTION_OBJECTS", + "NONE,MOTION", + "MOTION,MOTION", + "MOTION,", + "", + ): + with self.subTest(payload=payload): + assert birdseye_modes_from_mqtt_payload(payload) is None + + +class TestBirdseyeReviewSeverity(unittest.TestCase): + """Test that review updates drive the per-camera severity map.""" + + def setUp(self): + self.birdseye = Birdseye.__new__(Birdseye) + self.birdseye.review_subscriber = Mock() + self.birdseye.review_severity = {} + self.birdseye.birdseye_manager = Mock() + self.birdseye.birdseye_manager.update.return_value = (False, False) + self.birdseye._idle_interval = None + + def _drain(self, *updates): + self.birdseye.review_subscriber.check_for_update.side_effect = [*updates, None] + self.birdseye.check_review_updates() + + def test_new_segment_sets_severity(self): + self._drain({"type": "new", "after": {"camera": "front", "severity": "alert"}}) + + assert self.birdseye.review_severity == {"front": "alert"} + + def test_update_upgrades_severity(self): + self._drain( + {"type": "new", "after": {"camera": "front", "severity": "detection"}}, + {"type": "update", "after": {"camera": "front", "severity": "alert"}}, + ) + + assert self.birdseye.review_severity == {"front": "alert"} + + def test_end_clears_severity(self): + self._drain( + {"type": "new", "after": {"camera": "front", "severity": "alert"}}, + {"type": "end", "after": {"camera": "front", "severity": "alert"}}, + ) + + assert self.birdseye.review_severity == {} + + def test_write_data_passes_the_tracked_severity(self): + self.birdseye.review_severity = {"front": "alert"} + frame = Mock() + + self.birdseye.write_data("front", [], [], 1.0, frame) + + self.birdseye.birdseye_manager.update.assert_called_once_with( + "front", + BirdseyeActivity(has_object=False, has_motion=False, severity="alert"), + 1.0, + frame, + ) diff --git a/frigate/test/test_config.py b/frigate/test/test_config.py index 31e283eae2..585064a761 100644 --- a/frigate/test/test_config.py +++ b/frigate/test/test_config.py @@ -8,7 +8,7 @@ import numpy as np from pydantic import ValidationError from ruamel.yaml.constructor import DuplicateKeyError -from frigate.config import FrigateConfig, RetainModeEnum +from frigate.config import BirdseyeModeEnum, FrigateConfig, RetainModeEnum from frigate.const import MODEL_CACHE_DIR from frigate.detectors import DetectorTypeEnum from frigate.util.builtin import deep_merge @@ -171,7 +171,7 @@ class TestConfig(unittest.TestCase): def test_override_birdseye(self): config = { "mqtt": {"host": "mqtt"}, - "birdseye": {"enabled": True, "mode": {"continuous": True}}, + "birdseye": {"enabled": True, "modes": ["continuous"]}, "cameras": { "back": { "ffmpeg": { @@ -186,7 +186,7 @@ class TestConfig(unittest.TestCase): }, "birdseye": { "enabled": False, - "mode": {"continuous": False, "motion": True}, + "modes": ["motion"], }, } }, @@ -194,18 +194,16 @@ class TestConfig(unittest.TestCase): frigate_config = FrigateConfig(**config) assert not frigate_config.cameras["back"].birdseye.enabled - mode = frigate_config.cameras["back"].birdseye.mode - assert mode.motion - assert not mode.continuous - assert not mode.objects - assert not mode.stationary_objects + assert frigate_config.cameras["back"].birdseye.modes == [ + BirdseyeModeEnum.motion + ] def test_override_birdseye_non_inheritable(self): config = { "mqtt": {"host": "mqtt"}, "birdseye": { "enabled": True, - "mode": {"continuous": True}, + "modes": ["continuous"], "height": 1920, }, "cameras": { @@ -229,109 +227,38 @@ class TestConfig(unittest.TestCase): def test_inherit_birdseye(self): config = { - "mqtt": {"host": "mqtt"}, - "birdseye": {"enabled": True, "mode": {"continuous": True}}, - "cameras": { - "back": { - "ffmpeg": { - "inputs": [ - {"path": "rtsp://10.0.0.1:554/video", "roles": ["detect"]} - ] - }, - "detect": { - "height": 1080, - "width": 1920, - "fps": 5, - }, - } - }, + **self.minimal, + "birdseye": {"enabled": True, "modes": ["continuous"]}, } frigate_config = FrigateConfig(**config) assert frigate_config.cameras["back"].birdseye.enabled - mode = frigate_config.cameras["back"].birdseye.mode - assert mode.continuous - assert not mode.motion - assert not mode.objects - assert not mode.stationary_objects + assert frigate_config.cameras["back"].birdseye.modes == [ + BirdseyeModeEnum.continuous + ] - def test_combine_birdseye_activity_types(self): + def test_camera_modes_replace_the_global_list(self): + """A camera list fully replaces the global one, it does not merge into it.""" config = { **self.minimal, - "birdseye": { - "mode": { - "motion": True, - "stationary_objects": True, - } - }, + "birdseye": {"modes": ["motion", "all_objects"]}, } + config["cameras"]["back"]["birdseye"] = {"modes": ["alerts"]} frigate_config = FrigateConfig(**config) - mode = frigate_config.cameras["back"].birdseye.mode - assert mode.motion - assert mode.stationary_objects - assert not mode.continuous - assert not mode.objects + assert frigate_config.cameras["back"].birdseye.modes == [ + BirdseyeModeEnum.alerts + ] - def test_birdseye_requires_an_activity_type(self): + def test_camera_can_select_no_modes(self): config = { **self.minimal, - "birdseye": { - "mode": { - "continuous": False, - "motion": False, - "objects": False, - "stationary_objects": False, - } - }, + "birdseye": {"modes": ["motion"]}, } - - with self.assertRaisesRegex( - ValidationError, "must enable at least one Birdseye activity type" - ): - FrigateConfig(**config) - - def test_camera_can_disable_an_inherited_activity_type(self): - config = { - **self.minimal, - "birdseye": {"mode": {"motion": True, "objects": True}}, - } - config["cameras"]["back"]["birdseye"] = {"mode": {"motion": False}} + config["cameras"]["back"]["birdseye"] = {"modes": []} frigate_config = FrigateConfig(**config) - mode = frigate_config.cameras["back"].birdseye.mode - assert not mode.motion - assert mode.objects - - def test_profile_must_leave_an_activity_type_enabled(self): - config = { - **self.minimal, - "profiles": {"away": {"friendly_name": "Away"}}, - "birdseye": {"mode": {"objects": True}}, - } - config["cameras"]["back"]["profiles"] = { - "away": {"birdseye": {"mode": {"objects": False}}} - } - - with self.assertRaisesRegex( - ValidationError, "must enable at least one Birdseye activity type" - ): - FrigateConfig(**config) - - def test_camera_birdseye_activity_types_override_global_values(self): - config = { - **self.minimal, - "birdseye": {"mode": {"motion": True, "objects": True}}, - } - config["cameras"]["back"]["birdseye"] = { - "mode": {"motion": False, "stationary_objects": True} - } - - frigate_config = FrigateConfig(**config) - mode = frigate_config.cameras["back"].birdseye.mode - assert not mode.motion - assert mode.objects - assert mode.stationary_objects + assert frigate_config.cameras["back"].birdseye.modes == [] def test_override_tracked_objects(self): config = { diff --git a/frigate/test/test_dispatcher_runtime_state.py b/frigate/test/test_dispatcher_runtime_state.py index 0b9a3ff3fc..e8a1382d73 100644 --- a/frigate/test/test_dispatcher_runtime_state.py +++ b/frigate/test/test_dispatcher_runtime_state.py @@ -8,7 +8,7 @@ from unittest.mock import MagicMock, patch from frigate.app import FrigateApp from frigate.comms.dispatcher import Dispatcher from frigate.comms.runtime_state import RuntimeStatePersistence -from frigate.config import BirdseyeModeConfig +from frigate.config import BirdseyeModeEnum def _make_camera_mock( @@ -53,7 +53,7 @@ def _build_dispatcher(cameras: dict[str, MagicMock]) -> Dispatcher: class TestBirdseyeModeCommands(unittest.TestCase): - """Verify Birdseye mode commands use the boolean mode contract.""" + """Verify Birdseye mode commands use the activity list contract.""" def setUp(self) -> None: self.camera = _make_camera_mock() @@ -62,30 +62,33 @@ class TestBirdseyeModeCommands(unittest.TestCase): self.dispatcher.publish = MagicMock() def test_combined_modes_are_accepted(self) -> None: - self.dispatcher._on_birdseye_mode_command( - "front_door", "STATIONARY_OBJECTS,MOTION" - ) + self.dispatcher._on_birdseye_modes_command("front_door", "ALERTS,MOTION") self.assertEqual( - self.camera.birdseye.mode, - BirdseyeModeConfig(motion=True, stationary_objects=True), + self.camera.birdseye.modes, + [BirdseyeModeEnum.alerts, BirdseyeModeEnum.motion], ) self.dispatcher.config_updater.publish_update.assert_called_once() self.dispatcher.publish.assert_called_once_with( - "front_door/birdseye_mode/state", - "MOTION,STATIONARY_OBJECTS", + "front_door/birdseye_modes/state", + "MOTION,ALERTS", retain=True, ) def test_single_activity_type_is_accepted(self) -> None: - self.dispatcher._on_birdseye_mode_command("front_door", "OBJECTS") + self.dispatcher._on_birdseye_modes_command("front_door", "ALL_OBJECTS") - self.assertEqual( - self.camera.birdseye.mode, - BirdseyeModeConfig(objects=True), - ) + self.assertEqual(self.camera.birdseye.modes, [BirdseyeModeEnum.all_objects]) self.dispatcher.publish.assert_called_once_with( - "front_door/birdseye_mode/state", "OBJECTS", retain=True + "front_door/birdseye_modes/state", "ALL_OBJECTS", retain=True + ) + + def test_none_clears_every_activity_type(self) -> None: + self.dispatcher._on_birdseye_modes_command("front_door", "NONE") + + self.assertEqual(self.camera.birdseye.modes, []) + self.dispatcher.publish.assert_called_once_with( + "front_door/birdseye_modes/state", "NONE", retain=True ) def test_unknown_mode_is_rejected(self) -> None: @@ -93,13 +96,12 @@ class TestBirdseyeModeCommands(unittest.TestCase): "UNKNOWN", "motion", "MOTION_OBJECTS", - "NONE", "NONE,MOTION", "MOTION,MOTION", "MOTION,", ): with self.subTest(payload=payload): - self.dispatcher._on_birdseye_mode_command("front_door", payload) + self.dispatcher._on_birdseye_modes_command("front_door", payload) self.dispatcher.config_updater.publish_update.assert_not_called() diff --git a/frigate/test/test_profiles.py b/frigate/test/test_profiles.py index 412bca4c7b..fd949f76b3 100644 --- a/frigate/test/test_profiles.py +++ b/frigate/test/test_profiles.py @@ -6,7 +6,7 @@ import os import unittest from unittest.mock import MagicMock, patch -from frigate.config import FrigateConfig +from frigate.config import BirdseyeModeEnum, FrigateConfig from frigate.config.camera.profile import CameraProfileConfig from frigate.config.profile import ProfileDefinitionConfig from frigate.config.profile_manager import PERSISTENCE_FILE, ProfileManager @@ -561,23 +561,22 @@ class TestProfileManager(unittest.TestCase): assert self.config.cameras["front"].enabled is False @patch.object(ProfileManager, "_persist_active_profile") - def test_profile_can_disable_inherited_birdseye_activity(self, mock_persist): - """A false-only mode override inherits the other base activity types.""" + def test_profile_replaces_birdseye_activity_modes(self, mock_persist): + """A profile mode list replaces the base list rather than merging into it.""" self.config.profiles["away"] = ProfileDefinitionConfig(friendly_name="Away") - base_mode = self.config.cameras["front"].birdseye.mode - base_mode.motion = True - base_mode.objects = True + self.config.cameras["front"].birdseye.modes = [ + BirdseyeModeEnum.motion, + BirdseyeModeEnum.all_objects, + ] self.config.cameras["front"].profiles["away"] = CameraProfileConfig( - birdseye={"mode": {"motion": False}} + birdseye={"modes": ["alerts"]} ) self.manager = ProfileManager(self.config, self.mock_updater) err = self.manager.activate_profile("away") assert err is None - mode = self.config.cameras["front"].birdseye.mode - assert not mode.motion - assert mode.objects + assert self.config.cameras["front"].birdseye.modes == [BirdseyeModeEnum.alerts] @patch.object(ProfileManager, "_persist_active_profile") def test_deactivate_restores_enabled(self, mock_persist): diff --git a/frigate/util/config.py b/frigate/util/config.py index e256630daa..1bb5b5f38d 100644 --- a/frigate/util/config.py +++ b/frigate/util/config.py @@ -533,19 +533,26 @@ def _convert_legacy_mask_to_dict( return result +# the 0.18 scalar Birdseye modes, mapped to their activity mode names +_LEGACY_BIRDSEYE_MODES = { + "continuous": "continuous", + "motion": "motion", + "objects": "all_objects", +} + + def _migrate_birdseye_mode(birdseye: dict[str, Any] | None) -> None: - """Convert a scalar Birdseye mode to composable activity types.""" + """Convert a scalar Birdseye mode to an activity mode list.""" if not birdseye or not isinstance(birdseye.get("mode"), str): return legacy_mode = birdseye["mode"] - activity_types = ("continuous", "motion", "objects", "stationary_objects") - if legacy_mode not in activity_types: + + if legacy_mode not in _LEGACY_BIRDSEYE_MODES: return - birdseye["mode"] = { - activity_type: activity_type == legacy_mode for activity_type in activity_types - } + del birdseye["mode"] + birdseye["modes"] = [_LEGACY_BIRDSEYE_MODES[legacy_mode]] def migrate_018_0(config: dict[str, dict[str, Any]]) -> dict[str, dict[str, Any]]: diff --git a/web/e2e/fixtures/mock-data/config-schema.json b/web/e2e/fixtures/mock-data/config-schema.json index fcf280374f..84b67ced77 100644 --- a/web/e2e/fixtures/mock-data/config-schema.json +++ b/web/e2e/fixtures/mock-data/config-schema.json @@ -1 +1 @@ -{"$defs": {"AlertsConfig": {"additionalProperties": false, "description": "Configure alerts", "properties": {"enabled": {"default": true, "description": "Enable or disable alert generation for all cameras; can be overridden per-camera.", "title": "Enable alerts", "type": "boolean"}, "labels": {"default": ["person", "car"], "description": "List of object labels that qualify as alerts (for example: car, person).", "items": {"type": "string"}, "title": "Alert labels", "type": "array"}, "required_zones": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Zones that an object must enter to be considered an alert; leave empty to allow any zone.", "title": "Required zones"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Tracks whether alerts were originally enabled in the static configuration.", "title": "Original alerts state"}, "cutoff_time": {"default": 40, "description": "Seconds to wait after no alert-causing activity before cutting off an alert.", "title": "Alerts cutoff time", "type": "integer"}}, "title": "AlertsConfig", "type": "object"}, "AudioConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable audio event detection for all cameras; can be overridden per-camera.", "title": "Enable audio detection", "type": "boolean"}, "max_not_heard": {"default": 30, "description": "Amount of seconds without the configured audio type before the audio event is ended.", "title": "End timeout", "type": "integer"}, "min_volume": {"default": 500, "description": "Minimum RMS volume threshold required to run audio detection; lower values increase sensitivity (e.g., 200 high, 500 medium, 1000 low).", "title": "Minimum volume", "type": "integer"}, "listen": {"default": ["bark", "fire_alarm", "speech", "yell"], "description": "List of audio event types to detect (for example: bark, fire_alarm, speech, yell).", "items": {"type": "string"}, "title": "Listen types", "type": "array"}, "filters": {"anyOf": [{"additionalProperties": {"$ref": "#/$defs/AudioFilterConfig"}, "type": "object"}, {"type": "null"}], "default": null, "description": "Per-audio-type filter settings such as confidence thresholds used to reduce false positives.", "title": "Audio filters"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether audio detection was originally enabled in the static config file.", "title": "Original audio state"}, "num_threads": {"default": 2, "description": "Number of threads to use for audio detection processing.", "minimum": 1, "title": "Detection threads", "type": "integer"}}, "title": "AudioConfig", "type": "object"}, "AudioFilterConfig": {"additionalProperties": false, "properties": {"threshold": {"default": 0.8, "description": "Minimum confidence threshold for the audio event to be counted.", "exclusiveMaximum": 1.0, "minimum": 0.5, "title": "Minimum audio confidence", "type": "number"}}, "title": "AudioFilterConfig", "type": "object"}, "AudioTranscriptionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable automatic audio transcription for all cameras; can be overridden per-camera.", "title": "Enable audio transcription", "type": "boolean"}, "language": {"default": "en", "description": "Language code used for transcription/translation (for example 'en' for English). See https://whisper-api.com/docs/languages/ for supported language codes.", "title": "Transcription language", "type": "string"}, "device": {"$ref": "#/$defs/EnrichmentsDeviceEnum", "default": "CPU", "description": "Device key (CPU/GPU) to run the transcription model on. Only NVIDIA CUDA GPUs are currently supported for transcription.", "title": "Transcription device"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Model size to use for offline audio event transcription.", "title": "Model size"}, "live_enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "Enable streaming live transcription for audio as it is received.", "title": "Live transcription"}}, "title": "AudioTranscriptionConfig", "type": "object"}, "AuthConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable native authentication for the Frigate UI.", "title": "Enable authentication", "type": "boolean"}, "reset_admin_password": {"default": false, "description": "If true, reset the admin user's password on startup and print the new password in logs.", "title": "Reset admin password", "type": "boolean"}, "cookie_name": {"default": "frigate_token", "description": "Name of the cookie used to store the JWT token for native authentication.", "pattern": "^[a-z_]+$", "title": "JWT cookie name", "type": "string"}, "cookie_secure": {"default": false, "description": "Set the secure flag on the auth cookie; should be true when using TLS.", "title": "Secure cookie flag", "type": "boolean"}, "session_length": {"default": 86400, "description": "Session duration in seconds for JWT-based sessions.", "minimum": 60, "title": "Session length", "type": "integer"}, "refresh_time": {"default": 1800, "description": "When a session is within this many seconds of expiring, refresh it back to full length.", "minimum": 30, "title": "Session refresh window", "type": "integer"}, "failed_login_rate_limit": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Rate limiting rules for failed login attempts to reduce brute-force attacks.", "title": "Failed login limits"}, "trusted_proxies": {"default": [], "description": "List of trusted proxy IPs used when determining client IP for rate limiting.", "items": {"type": "string"}, "title": "Trusted proxies", "type": "array"}, "hash_iterations": {"default": 600000, "description": "Number of PBKDF2-SHA256 iterations to use when hashing user passwords.", "title": "Hash iterations", "type": "integer"}, "roles": {"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "description": "Map roles to camera lists. An empty list grants access to all cameras for the role.", "title": "Role mappings", "type": "object"}, "admin_first_time_login": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "When true the UI may show a help link on the login page informing users how to sign in after an admin password reset. ", "title": "First-time admin flag"}}, "title": "AuthConfig", "type": "object"}, "BaseDetectorConfig": {"additionalProperties": true, "properties": {"type": {"default": "cpu", "description": "Type of detector to use for object detection (for example 'cpu', 'edgetpu', 'openvino').", "title": "Detector Type", "type": "string"}, "model": {"anyOf": [{"$ref": "#/$defs/ModelConfig"}, {"type": "null"}], "default": null, "description": "Detector-specific model configuration options (path, input size, etc.).", "title": "Detector specific model configuration"}, "model_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "File path to the detector model binary if required by the chosen detector.", "title": "Detector specific model path"}}, "title": "BaseDetectorConfig", "type": "object"}, "BirdClassificationConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable bird classification.", "title": "Bird classification", "type": "boolean"}, "threshold": {"default": 0.9, "description": "Minimum classification score required to accept a bird classification.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Minimum score", "type": "number"}}, "title": "BirdClassificationConfig", "type": "object"}, "BirdseyeCameraConfig": {"properties": {"enabled": {"default": true, "description": "Enable or disable the Birdseye view feature.", "title": "Enable Birdseye", "type": "boolean"}, "mode": {"$ref": "#/$defs/BirdseyeModeConfig", "description": "Activity types that include cameras in Birdseye.", "title": "Activity types"}, "order": {"default": 0, "description": "Numeric position controlling the camera's ordering in the Birdseye layout.", "title": "Position", "type": "integer"}}, "title": "BirdseyeCameraConfig", "type": "object"}, "BirdseyeConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable the Birdseye view feature.", "title": "Enable Birdseye", "type": "boolean"}, "mode": {"$ref": "#/$defs/BirdseyeModeConfig", "description": "Activity types that include cameras in Birdseye.", "title": "Activity types"}, "restream": {"default": false, "description": "Re-stream the Birdseye output as an RTSP feed; enabling this will keep Birdseye running continuously.", "title": "Restream RTSP", "type": "boolean"}, "width": {"default": 1280, "description": "Output width (pixels) of the composed Birdseye frame.", "title": "Width", "type": "integer"}, "height": {"default": 720, "description": "Output height (pixels) of the composed Birdseye frame.", "title": "Height", "type": "integer"}, "quality": {"default": 8, "description": "Encoding quality for the Birdseye mpeg1 feed (1 highest quality, 31 lowest).", "maximum": 31, "minimum": 1, "title": "Encoding quality", "type": "integer"}, "inactivity_threshold": {"default": 30, "description": "Seconds of inactivity after which a camera will stop being shown in Birdseye.", "exclusiveMinimum": 0, "title": "Inactivity threshold", "type": "integer"}, "layout": {"$ref": "#/$defs/BirdseyeLayoutConfig", "description": "Layout options for the Birdseye composition.", "title": "Layout"}, "idle_heartbeat_fps": {"default": 0.0, "description": "Frames-per-second to resend the last composed Birdseye frame when idle; set to 0 to disable.", "maximum": 10.0, "minimum": 0.0, "title": "Idle heartbeat FPS", "type": "number"}}, "title": "BirdseyeConfig", "type": "object"}, "BirdseyeLayoutConfig": {"additionalProperties": false, "properties": {"scaling_factor": {"default": 2.0, "description": "Scaling factor used by the layout calculator (range 1.0 to 5.0).", "maximum": 5.0, "minimum": 1.0, "title": "Scaling factor", "type": "number"}, "max_cameras": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Maximum number of cameras to display at once in Birdseye; shows the most recent cameras.", "title": "Max cameras"}}, "title": "BirdseyeLayoutConfig", "type": "object"}, "BirdseyeModeConfig": {"additionalProperties": false, "properties": {"continuous": {"default": false, "description": "Always include the camera in Birdseye.", "title": "Continuous", "type": "boolean"}, "motion": {"default": false, "description": "Include the camera in Birdseye when motion is detected.", "title": "Motion", "type": "boolean"}, "objects": {"default": false, "description": "Include the camera in Birdseye while an active object is tracked.", "title": "Active objects", "type": "boolean"}, "stationary_objects": {"default": false, "description": "Include the camera in Birdseye while a stationary object is tracked.", "title": "Stationary objects", "type": "boolean"}}, "title": "BirdseyeModeConfig", "type": "object"}, "CameraAudioTranscriptionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable manually triggered audio event transcription.", "title": "Enable transcription", "type": "boolean"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Original transcription state"}, "live_enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "Enable streaming live transcription for audio as it is received.", "title": "Live transcription"}}, "title": "CameraAudioTranscriptionConfig", "type": "object"}, "CameraConfig": {"additionalProperties": false, "properties": {"name": {"anyOf": [{"pattern": "^[a-zA-Z0-9_-]+$", "type": "string"}, {"type": "null"}], "default": null, "description": "Camera name is required", "title": "Camera name"}, "friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Camera friendly name used in the Frigate UI", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enabled", "title": "Enabled", "type": "boolean"}, "audio": {"$ref": "#/$defs/AudioConfig", "description": "Settings for audio-based event detection for this camera.", "title": "Audio detection"}, "audio_transcription": {"$ref": "#/$defs/CameraAudioTranscriptionConfig", "description": "Settings for live and speech audio transcription used for events and live captions.", "title": "Audio transcription"}, "birdseye": {"$ref": "#/$defs/BirdseyeCameraConfig", "description": "Settings for the Birdseye composite view that composes multiple camera feeds into a single layout.", "title": "Birdseye"}, "detect": {"$ref": "#/$defs/DetectConfig", "description": "Settings for the detection/detect role used to run object detection and initialize trackers.", "title": "Object Detection"}, "face_recognition": {"$ref": "#/$defs/CameraFaceRecognitionConfig", "description": "Settings for face detection and recognition for this camera.", "title": "Face recognition"}, "ffmpeg": {"$ref": "#/$defs/CameraFfmpegConfig", "description": "Camera stream inputs and FFmpeg options, including binary path, args, hwaccel, and per-role output args.", "title": "Streams (FFmpeg)"}, "live": {"$ref": "#/$defs/CameraLiveConfig", "description": "Settings used by the Web UI to control live stream selection, resolution and quality.", "title": "Live playback"}, "lpr": {"$ref": "#/$defs/CameraLicensePlateRecognitionConfig", "description": "License plate recognition settings including detection thresholds, formatting, and known plates.", "title": "License Plate Recognition"}, "motion": {"$ref": "#/$defs/MotionConfig", "default": null, "description": "Default motion detection settings for this camera.", "title": "Motion detection"}, "objects": {"$ref": "#/$defs/ObjectConfig", "description": "Object tracking defaults including which labels to track and per-object filters.", "title": "Objects"}, "record": {"$ref": "#/$defs/RecordConfig", "description": "Recording and retention settings for this camera.", "title": "Recording"}, "review": {"$ref": "#/$defs/ReviewConfig", "description": "Settings that control alerts, detections, and GenAI review summaries used by the UI and storage for this camera.", "title": "Review"}, "semantic_search": {"$ref": "#/$defs/CameraSemanticSearchConfig", "description": "Settings for semantic search which builds and queries object embeddings to find similar items.", "title": "Semantic Search"}, "snapshots": {"$ref": "#/$defs/SnapshotsConfig", "description": "Settings for API-generated snapshots of tracked objects for this camera.", "title": "Snapshots"}, "timestamp_style": {"$ref": "#/$defs/TimestampStyleConfig", "description": "Styling options for timestamps applied to snapshots and Debug view.", "title": "Timestamp style"}, "best_image_timeout": {"default": 60, "description": "How long to wait for the image with the highest confidence score.", "title": "Best image timeout", "type": "integer"}, "mqtt": {"$ref": "#/$defs/CameraMqttConfig", "description": "MQTT image publishing settings.", "title": "MQTT"}, "notifications": {"$ref": "#/$defs/NotificationConfig", "description": "Settings to enable and control notifications for this camera.", "title": "Notifications"}, "onvif": {"$ref": "#/$defs/OnvifConfig", "description": "ONVIF connection and PTZ autotracking settings for this camera.", "title": "ONVIF"}, "type": {"$ref": "#/$defs/CameraTypeEnum", "default": "generic", "description": "Camera Type", "title": "Camera type"}, "ui": {"$ref": "#/$defs/CameraUiConfig", "description": "Display ordering and visibility for this camera in the UI. Ordering affects the default dashboard. For more granular control, use camera groups.", "title": "Camera UI"}, "webui_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "URL to visit the camera directly from system page", "title": "Camera URL"}, "profiles": {"additionalProperties": {"$ref": "#/$defs/CameraProfileConfig"}, "description": "Named config profiles with partial overrides that can be activated at runtime.", "title": "Profiles", "type": "object"}, "zones": {"additionalProperties": {"$ref": "#/$defs/ZoneConfig"}, "description": "Zones allow you to define a specific area of the frame so you can determine whether or not an object is within a particular area.", "title": "Zones", "type": "object"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Keep track of original state of camera.", "title": "Original camera state"}}, "required": ["ffmpeg"], "title": "CameraConfig", "type": "object"}, "CameraFaceRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable face recognition.", "title": "Enable face recognition", "type": "boolean"}, "min_area": {"default": 750, "description": "Minimum area (pixels) of a detected face box required to attempt recognition.", "title": "Minimum face area", "type": "integer"}}, "title": "CameraFaceRecognitionConfig", "type": "object"}, "CameraFfmpegConfig": {"additionalProperties": false, "properties": {"path": {"default": "default", "description": "Path to the FFmpeg binary to use or a version alias (\"7.0\" or \"8.0\").", "title": "FFmpeg path", "type": "string"}, "global_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "description": "Global arguments passed to FFmpeg processes.", "title": "FFmpeg global arguments"}, "hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "auto", "description": "Hardware acceleration arguments for FFmpeg. Provider-specific presets are recommended.", "title": "Hardware acceleration arguments"}, "input_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "preset-rtsp-generic", "description": "Input arguments applied to FFmpeg input streams.", "title": "Input arguments"}, "output_args": {"$ref": "#/$defs/FfmpegOutputArgsConfig", "description": "Default output arguments used for different FFmpeg roles such as detect and record.", "title": "Output arguments"}, "retry_interval": {"default": 10.0, "description": "Seconds to wait before attempting to reconnect a camera stream after failure. Default is 10.", "exclusiveMinimum": 0.0, "title": "FFmpeg retry time", "type": "number"}, "apple_compatibility": {"default": false, "description": "Enable HEVC tagging for better Apple player compatibility when recording H.265.", "title": "Apple compatibility", "type": "boolean"}, "gpu": {"default": 0, "description": "Default GPU index used for hardware acceleration if available.", "title": "GPU index", "type": "integer"}, "inputs": {"description": "List of input stream definitions (paths and roles) for this camera.", "items": {"$ref": "#/$defs/CameraInput"}, "title": "Camera inputs", "type": "array"}}, "required": ["inputs"], "title": "CameraFfmpegConfig", "type": "object"}, "CameraGroupConfig": {"additionalProperties": false, "properties": {"cameras": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Array of camera names included in this group.", "title": "Camera list"}, "icon": {"default": "generic", "description": "Icon used to represent the camera group in the UI.", "title": "Group icon", "type": "string"}, "order": {"default": 0, "description": "Numeric order used to sort camera groups in the UI; larger numbers appear later.", "title": "Sort order", "type": "integer"}}, "title": "CameraGroupConfig", "type": "object"}, "CameraInput": {"additionalProperties": false, "properties": {"path": {"description": "Camera input stream URL or path.", "title": "Input path", "type": "string"}, "roles": {"description": "Roles for this input stream.", "items": {"$ref": "#/$defs/CameraRoleEnum"}, "title": "Input roles", "type": "array"}, "global_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "FFmpeg global arguments for this input stream.", "title": "FFmpeg global arguments"}, "hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Hardware acceleration arguments for this input stream.", "title": "Hardware acceleration arguments"}, "input_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Input arguments specific to this stream.", "title": "Input arguments"}}, "required": ["path", "roles"], "title": "CameraInput", "type": "object"}, "CameraLicensePlateRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable LPR on this camera.", "title": "Enable LPR", "type": "boolean"}, "expire_time": {"default": 3, "description": "Time in seconds after which an unseen plate is expired from the tracker (for dedicated LPR cameras only).", "exclusiveMinimum": 0, "title": "Expire seconds", "type": "integer"}, "min_area": {"default": 1000, "description": "Minimum plate area (pixels) required to attempt recognition.", "title": "Minimum plate area", "type": "integer"}, "enhancement": {"default": 0, "description": "Enhancement level (0-10) to apply to plate crops prior to OCR; higher values may not always improve results, levels above 5 may only work with night time plates and should be used with caution.", "maximum": 10, "minimum": 0, "title": "Enhancement level", "type": "integer"}}, "title": "CameraLicensePlateRecognitionConfig", "type": "object"}, "CameraLiveConfig": {"additionalProperties": false, "properties": {"streams": {"additionalProperties": {"type": "string"}, "description": "Mapping of configured stream names to restream/go2rtc names used for live playback.", "title": "Live stream names", "type": "object"}, "height": {"default": 720, "description": "Height (pixels) to render the jsmpeg live stream in the Web UI; must be <= detect stream height.", "title": "Live height", "type": "integer"}, "quality": {"default": 8, "description": "Encoding quality for the jsmpeg stream (1 highest, 31 lowest).", "maximum": 31, "minimum": 1, "title": "Live quality", "type": "integer"}}, "title": "CameraLiveConfig", "type": "object"}, "CameraMqttConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable publishing image snapshots for objects to MQTT topics for this camera.", "title": "Send image", "type": "boolean"}, "timestamp": {"default": true, "description": "Overlay a timestamp on images published to MQTT.", "title": "Add timestamp", "type": "boolean"}, "bounding_box": {"default": true, "description": "Draw bounding boxes on images published over MQTT.", "title": "Add bounding box", "type": "boolean"}, "crop": {"default": true, "description": "Crop images published to MQTT to the detected object's bounding box.", "title": "Crop image", "type": "boolean"}, "height": {"default": 270, "description": "Height (pixels) to resize images published over MQTT.", "title": "Image height", "type": "integer"}, "required_zones": {"description": "Zones that an object must enter for an MQTT image to be published.", "items": {"type": "string"}, "title": "Required zones", "type": "array"}, "quality": {"default": 70, "description": "JPEG quality for images published to MQTT (0-100).", "maximum": 100, "minimum": 0, "title": "JPEG quality", "type": "integer"}}, "title": "CameraMqttConfig", "type": "object"}, "CameraProfileConfig": {"additionalProperties": false, "description": "A named profile containing partial camera config overrides.\n\nSections set to None inherit from the camera's base config.\nSections that are defined get Pydantic-validated, then only\nexplicitly-set fields are used as overrides via exclude_unset.", "properties": {"enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Enabled"}, "audio": {"anyOf": [{"$ref": "#/$defs/AudioConfig"}, {"type": "null"}], "default": null}, "birdseye": {"anyOf": [{"$ref": "#/$defs/BirdseyeCameraConfig"}, {"type": "null"}], "default": null}, "detect": {"anyOf": [{"$ref": "#/$defs/DetectConfig"}, {"type": "null"}], "default": null}, "face_recognition": {"anyOf": [{"$ref": "#/$defs/CameraFaceRecognitionConfig"}, {"type": "null"}], "default": null}, "lpr": {"anyOf": [{"$ref": "#/$defs/CameraLicensePlateRecognitionConfig"}, {"type": "null"}], "default": null}, "motion": {"anyOf": [{"$ref": "#/$defs/MotionConfig"}, {"type": "null"}], "default": null}, "notifications": {"anyOf": [{"$ref": "#/$defs/NotificationConfig"}, {"type": "null"}], "default": null}, "objects": {"anyOf": [{"$ref": "#/$defs/ObjectConfig"}, {"type": "null"}], "default": null}, "record": {"anyOf": [{"$ref": "#/$defs/RecordConfig"}, {"type": "null"}], "default": null}, "review": {"anyOf": [{"$ref": "#/$defs/ReviewConfig"}, {"type": "null"}], "default": null}, "snapshots": {"anyOf": [{"$ref": "#/$defs/SnapshotsConfig"}, {"type": "null"}], "default": null}, "zones": {"anyOf": [{"additionalProperties": {"$ref": "#/$defs/ZoneConfig"}, "type": "object"}, {"type": "null"}], "default": null, "title": "Zones"}}, "title": "CameraProfileConfig", "type": "object"}, "CameraRoleEnum": {"enum": ["audio", "record", "record_sub", "detect"], "title": "CameraRoleEnum", "type": "string"}, "CameraSemanticSearchConfig": {"additionalProperties": false, "properties": {"triggers": {"additionalProperties": {"$ref": "#/$defs/TriggerConfig"}, "default": {}, "description": "Actions and matching criteria for camera-specific semantic search triggers.", "title": "Triggers", "type": "object"}}, "title": "CameraSemanticSearchConfig", "type": "object"}, "CameraTypeEnum": {"enum": ["generic", "lpr"], "title": "CameraTypeEnum", "type": "string"}, "CameraUiConfig": {"additionalProperties": false, "properties": {"order": {"default": 0, "description": "Numeric order used to sort the camera in the UI (default dashboard and lists); larger numbers appear later.", "title": "UI order", "type": "integer"}, "dashboard": {"default": true, "description": "Toggle whether this camera is visible on the default All Cameras live dashboard. The camera remains available everywhere else in the UI, including camera groups and settings.", "title": "Show on Live dashboard", "type": "boolean"}, "review": {"default": true, "description": "Toggle whether this camera is visible in review (the review page and its camera filter, motion review, and the history view).", "title": "Show in review", "type": "boolean"}}, "title": "CameraUiConfig", "type": "object"}, "ChaptersEnum": {"enum": ["none", "recording_segments", "review_items"], "title": "ChaptersEnum", "type": "string"}, "ClassificationConfig": {"additionalProperties": false, "properties": {"bird": {"$ref": "#/$defs/BirdClassificationConfig", "description": "Settings specific to bird classification models.", "title": "Bird classification config"}, "custom": {"additionalProperties": {"$ref": "#/$defs/CustomClassificationConfig"}, "default": {}, "description": "Configuration for custom classification models used for objects or state detection.", "title": "Custom Classification Models", "type": "object"}}, "title": "ClassificationConfig", "type": "object"}, "ColorConfig": {"additionalProperties": false, "properties": {"red": {"default": 255, "description": "Red component (0-255) for timestamp color.", "maximum": 255, "minimum": 0, "title": "Red", "type": "integer"}, "green": {"default": 255, "description": "Green component (0-255) for timestamp color.", "maximum": 255, "minimum": 0, "title": "Green", "type": "integer"}, "blue": {"default": 255, "description": "Blue component (0-255) for timestamp color.", "maximum": 255, "minimum": 0, "title": "Blue", "type": "integer"}}, "title": "ColorConfig", "type": "object"}, "CustomClassificationConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable the custom classification model.", "title": "Enable model", "type": "boolean"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Identifier for the custom classification model to use.", "title": "Model name"}, "threshold": {"default": 0.8, "description": "Score threshold used to change the classification state.", "title": "Score threshold", "type": "number"}, "save_attempts": {"anyOf": [{"minimum": 0, "type": "integer"}, {"type": "null"}], "default": null, "description": "How many classification attempts to save for recent classifications UI.", "title": "Save attempts"}, "object_config": {"anyOf": [{"$ref": "#/$defs/CustomClassificationObjectConfig"}, {"type": "null"}], "default": null}, "state_config": {"anyOf": [{"$ref": "#/$defs/CustomClassificationStateConfig"}, {"type": "null"}], "default": null}}, "title": "CustomClassificationConfig", "type": "object"}, "CustomClassificationObjectConfig": {"additionalProperties": false, "properties": {"objects": {"description": "List of object types to run object classification on.", "items": {"type": "string"}, "title": "Classify objects", "type": "array"}, "classification_type": {"$ref": "#/$defs/ObjectClassificationType", "default": "sub_label", "description": "Classification type applied: 'sub_label' (adds sub_label) or other supported types.", "title": "Classification type"}}, "title": "CustomClassificationObjectConfig", "type": "object"}, "CustomClassificationStateCameraConfig": {"additionalProperties": false, "properties": {"crop": {"description": "Crop coordinates to use for running classification on this camera.", "items": {"type": "number"}, "title": "Classification crop", "type": "array"}}, "required": ["crop"], "title": "CustomClassificationStateCameraConfig", "type": "object"}, "CustomClassificationStateConfig": {"additionalProperties": false, "properties": {"cameras": {"additionalProperties": {"$ref": "#/$defs/CustomClassificationStateCameraConfig"}, "description": "Per-camera crop and settings for running state classification.", "title": "Classification cameras", "type": "object"}, "motion": {"default": false, "description": "If true, run classification when motion is detected within the specified crop.", "title": "Run on motion", "type": "boolean"}, "interval": {"anyOf": [{"exclusiveMinimum": 0, "type": "integer"}, {"type": "null"}], "default": null, "description": "Interval (seconds) between periodic classification runs for state classification.", "title": "Classification interval"}}, "required": ["cameras"], "title": "CustomClassificationStateConfig", "type": "object"}, "DatabaseConfig": {"additionalProperties": false, "properties": {"path": {"default": "/config/frigate.db", "description": "Filesystem path where the Frigate SQLite database file will be stored.", "title": "Database path", "type": "string"}}, "title": "DatabaseConfig", "type": "object"}, "DetectConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable object detection for all cameras; can be overridden per-camera.", "title": "Enable object detection", "type": "boolean"}, "height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Height (pixels) of frames used for the detect stream; leave empty to use the native stream resolution.", "title": "Detect height"}, "width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Width (pixels) of frames used for the detect stream; leave empty to use the native stream resolution.", "title": "Detect width"}, "fps": {"default": 5, "description": "Desired frames per second to run detection on; lower values reduce CPU usage (recommended value is 5, only set higher - at most 10 - if tracking extremely fast moving objects).", "title": "Detect FPS", "type": "integer"}, "min_initialized": {"anyOf": [{"minimum": 2, "type": "integer"}, {"type": "null"}], "default": null, "description": "Number of consecutive detection hits required before creating a tracked object. Increase to reduce false initializations. Default value is fps divided by 2.", "title": "Minimum initialization frames"}, "max_disappeared": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Number of frames without a detection before a tracked object is considered gone.", "title": "Maximum disappeared frames"}, "stationary": {"$ref": "#/$defs/StationaryConfig", "description": "Settings to detect and manage objects that remain stationary for a period of time.", "title": "Stationary objects config"}, "annotation_offset": {"default": 0, "description": "Milliseconds to shift detect annotations to better align timeline bounding boxes with recordings; can be positive or negative.", "title": "Annotation offset", "type": "integer"}}, "title": "DetectConfig", "type": "object"}, "DetectionsConfig": {"additionalProperties": false, "description": "Configure detections", "properties": {"enabled": {"default": true, "description": "Enable or disable detection events for all cameras; can be overridden per-camera.", "title": "Enable detections", "type": "boolean"}, "labels": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "default": null, "description": "List of object labels that qualify as detection events.", "title": "Detection labels"}, "required_zones": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Zones that an object must enter to be considered a detection; leave empty to allow any zone.", "title": "Required zones"}, "cutoff_time": {"default": 30, "description": "Seconds to wait after no detection-causing activity before cutting off a detection.", "title": "Detections cutoff time", "type": "integer"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Tracks whether detections were originally enabled in the static configuration.", "title": "Original detections state"}}, "title": "DetectionsConfig", "type": "object"}, "EnrichmentsDeviceEnum": {"enum": ["GPU", "CPU"], "title": "EnrichmentsDeviceEnum", "type": "string"}, "EventsConfig": {"additionalProperties": false, "properties": {"pre_capture": {"default": 5, "description": "Number of seconds before the detection event to include in the recording.", "maximum": 60, "minimum": 0, "title": "Pre-capture seconds", "type": "integer"}, "post_capture": {"default": 5, "description": "Number of seconds after the detection event to include in the recording.", "minimum": 0, "title": "Post-capture seconds", "type": "integer"}, "retain": {"$ref": "#/$defs/ReviewRetainConfig", "description": "Retention settings for recordings of detection events.", "title": "Event retention"}}, "title": "EventsConfig", "type": "object"}, "FaceRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable face recognition for all cameras; can be overridden per-camera.", "title": "Enable face recognition", "type": "boolean"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Model size to use for face embeddings (small/large); larger may require GPU.", "title": "Model size"}, "unknown_score": {"default": 0.8, "description": "Distance threshold below which a face is considered a potential match (higher = stricter).", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Unknown score threshold", "type": "number"}, "detection_threshold": {"default": 0.7, "description": "Minimum detection confidence required to consider a face detection valid.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Detection threshold", "type": "number"}, "recognition_threshold": {"default": 0.9, "description": "Face embedding distance threshold to consider two faces a match.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Recognition threshold", "type": "number"}, "min_area": {"default": 750, "description": "Minimum area (pixels) of a detected face box required to attempt recognition.", "title": "Minimum face area", "type": "integer"}, "min_faces": {"default": 1, "description": "Minimum number of face recognitions required before applying a recognized sub-label to a person.", "exclusiveMinimum": 0, "maximum": 6, "title": "Minimum faces", "type": "integer"}, "save_attempts": {"default": 200, "description": "Number of face recognition attempts to retain for recent recognition UI.", "minimum": 0, "title": "Save attempts", "type": "integer"}, "blur_confidence_filter": {"default": true, "description": "Adjust confidence scores based on image blur to reduce false positives for poor quality faces.", "title": "Blur confidence filter", "type": "boolean"}, "device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information", "title": "Device"}}, "title": "FaceRecognitionConfig", "type": "object"}, "FfmpegConfig": {"additionalProperties": false, "properties": {"path": {"default": "default", "description": "Path to the FFmpeg binary to use or a version alias (\"7.0\" or \"8.0\").", "title": "FFmpeg path", "type": "string"}, "global_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "description": "Global arguments passed to FFmpeg processes.", "title": "FFmpeg global arguments"}, "hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "auto", "description": "Hardware acceleration arguments for FFmpeg. Provider-specific presets are recommended.", "title": "Hardware acceleration arguments"}, "input_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "preset-rtsp-generic", "description": "Input arguments applied to FFmpeg input streams.", "title": "Input arguments"}, "output_args": {"$ref": "#/$defs/FfmpegOutputArgsConfig", "description": "Default output arguments used for different FFmpeg roles such as detect and record.", "title": "Output arguments"}, "retry_interval": {"default": 10.0, "description": "Seconds to wait before attempting to reconnect a camera stream after failure. Default is 10.", "exclusiveMinimum": 0.0, "title": "FFmpeg retry time", "type": "number"}, "apple_compatibility": {"default": false, "description": "Enable HEVC tagging for better Apple player compatibility when recording H.265.", "title": "Apple compatibility", "type": "boolean"}, "gpu": {"default": 0, "description": "Default GPU index used for hardware acceleration if available.", "title": "GPU index", "type": "integer"}}, "title": "FfmpegConfig", "type": "object"}, "FfmpegOutputArgsConfig": {"additionalProperties": false, "properties": {"detect": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "description": "Default output arguments for detect role streams.", "title": "Detect output arguments"}, "record": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "preset-record-generic-audio-aac", "description": "Default output arguments for record role streams.", "title": "Record output arguments"}, "record_sub": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Output arguments for record_sub role streams. The record output arguments are used when this is not set.", "title": "Sub stream record output arguments"}}, "title": "FfmpegOutputArgsConfig", "type": "object"}, "FilterConfig": {"additionalProperties": false, "properties": {"min_area": {"anyOf": [{"type": "integer"}, {"type": "number"}], "default": 0, "description": "Minimum bounding box area (pixels or percentage) required for this object type. Can be pixels (int) or percentage (float between 0.000001 and 0.99).", "title": "Minimum object area"}, "max_area": {"anyOf": [{"type": "integer"}, {"type": "number"}], "default": 24000000, "description": "Maximum bounding box area (pixels or percentage) allowed for this object type. Can be pixels (int) or percentage (float between 0.000001 and 0.99).", "title": "Maximum object area"}, "min_ratio": {"default": 0, "description": "Minimum width/height ratio required for the bounding box to qualify.", "title": "Minimum aspect ratio", "type": "number"}, "max_ratio": {"default": 24000000, "description": "Maximum width/height ratio allowed for the bounding box to qualify.", "title": "Maximum aspect ratio", "type": "number"}, "threshold": {"default": 0.7, "description": "Average detection confidence threshold required for the object to be considered a true positive.", "title": "Confidence threshold", "type": "number"}, "min_score": {"default": 0.5, "description": "Minimum single-frame detection confidence required for the object to be counted.", "title": "Minimum confidence", "type": "number"}, "mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "description": "Polygon coordinates defining where this filter applies within the frame.", "title": "Filter mask", "type": "object"}, "raw_mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "title": "Raw Mask", "type": "object"}}, "title": "FilterConfig", "type": "object"}, "GenAIConfig": {"additionalProperties": false, "description": "Primary GenAI Config to define GenAI Provider.", "properties": {"api_key": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "API key required by some providers (can also be set via environment variables).", "title": "API key"}, "base_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Base URL for self-hosted or compatible providers (for example an Ollama instance).", "title": "Base URL"}, "model": {"default": "", "description": "The model to use from the provider for generating descriptions or summaries.", "title": "Model", "type": "string"}, "provider": {"$ref": "#/$defs/GenAIProviderEnum", "description": "The GenAI provider to use (for example: ollama, gemini, openai).", "title": "Provider"}, "roles": {"description": "GenAI roles (chat, descriptions, embeddings); one provider per role.", "items": {"$ref": "#/$defs/GenAIRoleEnum"}, "title": "Roles", "type": "array"}, "provider_options": {"additionalProperties": {}, "default": {}, "description": "Additional provider-specific options to pass to the GenAI client.", "title": "Provider options", "type": "object"}, "runtime_options": {"additionalProperties": {}, "default": {}, "description": "Runtime options passed to the provider for each inference call.", "title": "Runtime options", "type": "object"}}, "required": ["provider"], "title": "GenAIConfig", "type": "object"}, "GenAIObjectConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable GenAI generation of descriptions for tracked objects by default.", "title": "Enable GenAI", "type": "boolean"}, "use_snapshot": {"default": false, "description": "Use object snapshots instead of thumbnails for GenAI description generation.", "title": "Use snapshots", "type": "boolean"}, "prompt": {"default": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "description": "Default prompt template used when generating descriptions with GenAI.", "title": "Caption prompt", "type": "string"}, "object_prompts": {"additionalProperties": {"type": "string"}, "description": "Per-object prompts to customize GenAI outputs for specific labels.", "title": "Object prompts", "type": "object"}, "objects": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "List of object labels to send to GenAI by default.", "title": "GenAI objects"}, "required_zones": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Zones that must be entered for objects to qualify for GenAI description generation.", "title": "Required zones"}, "debug_save_thumbnails": {"default": false, "description": "Save thumbnails sent to GenAI for debugging and review.", "title": "Save thumbnails", "type": "boolean"}, "send_triggers": {"$ref": "#/$defs/GenAIObjectTriggerConfig", "description": "Defines when frames should be sent to GenAI (on end, after updates, etc.).", "title": "GenAI triggers"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether GenAI was enabled in the original static config.", "title": "Original GenAI state"}}, "title": "GenAIObjectConfig", "type": "object"}, "GenAIObjectTriggerConfig": {"additionalProperties": false, "properties": {"tracked_object_end": {"default": true, "description": "Send a request to GenAI when the tracked object ends.", "title": "Send on end", "type": "boolean"}, "after_significant_updates": {"anyOf": [{"minimum": 1, "type": "integer"}, {"type": "null"}], "default": null, "description": "Send a request to GenAI after a specified number of significant updates for the tracked object.", "title": "Early GenAI trigger"}}, "title": "GenAIObjectTriggerConfig", "type": "object"}, "GenAIProviderEnum": {"enum": ["openai", "azure_openai", "gemini", "ollama", "llamacpp"], "title": "GenAIProviderEnum", "type": "string"}, "GenAIReviewConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable GenAI-generated descriptions and summaries for review items.", "title": "Enable GenAI descriptions", "type": "boolean"}, "alerts": {"default": true, "description": "Use GenAI to generate descriptions for alert items.", "title": "Enable GenAI for alerts", "type": "boolean"}, "detections": {"default": false, "description": "Use GenAI to generate descriptions for detection items.", "title": "Enable GenAI for detections", "type": "boolean"}, "image_source": {"$ref": "#/$defs/ImageSourceEnum", "default": "preview", "description": "Source of images sent to GenAI ('preview' or 'recordings'); 'recordings' uses higher quality frames but more tokens.", "title": "Review image source"}, "additional_concerns": {"default": [], "description": "A list of additional concerns or notes the GenAI should consider when evaluating activity on this camera.", "items": {"type": "string"}, "title": "Additional concerns", "type": "array"}, "debug_save_thumbnails": {"default": false, "description": "Save thumbnails that are sent to the GenAI provider for debugging and review.", "title": "Save thumbnails", "type": "boolean"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Tracks whether GenAI review was originally enabled in the static configuration.", "title": "Original GenAI state"}, "preferred_language": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Preferred language to request from the GenAI provider for generated responses.", "title": "Preferred language"}, "activity_context_prompt": {"default": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is.", "description": "Custom prompt describing what is and is not suspicious activity to provide context for GenAI summaries.", "title": "Activity context prompt", "type": "string"}}, "title": "GenAIReviewConfig", "type": "object"}, "GenAIRoleEnum": {"enum": ["chat", "descriptions", "embeddings"], "title": "GenAIRoleEnum", "type": "string"}, "HeaderMappingConfig": {"additionalProperties": false, "properties": {"user": {"default": null, "description": "Header containing the authenticated username provided by the upstream proxy.", "title": "User header", "type": "string"}, "role": {"default": null, "description": "Header containing the authenticated user's role or groups from the upstream proxy.", "title": "Role header", "type": "string"}, "role_map": {"anyOf": [{"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "type": "object"}, {"type": "null"}], "description": "Map upstream group values to Frigate roles (for example map admin groups to the admin role).", "title": "Role mapping"}}, "title": "HeaderMappingConfig", "type": "object"}, "IPv6Config": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable IPv6 support for Frigate services (API and UI) where applicable.", "title": "Enable IPv6", "type": "boolean"}}, "title": "IPv6Config", "type": "object"}, "ImageSourceEnum": {"description": "Image source options for GenAI Review.", "enum": ["preview", "recordings"], "title": "ImageSourceEnum", "type": "string"}, "InputDTypeEnum": {"enum": ["float", "float_denorm", "int"], "title": "InputDTypeEnum", "type": "string"}, "InputTensorEnum": {"enum": ["nchw", "nhwc", "hwnc", "hwcn"], "title": "InputTensorEnum", "type": "string"}, "LicensePlateRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable license plate recognition for all cameras; can be overridden per-camera.", "title": "Enable LPR", "type": "boolean"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Model size used for text detection/recognition. Most users should use 'small'.", "title": "Model size"}, "detection_threshold": {"default": 0.7, "description": "Detection confidence threshold to begin running OCR on a suspected plate.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Detection threshold", "type": "number"}, "min_area": {"default": 1000, "description": "Minimum plate area (pixels) required to attempt recognition.", "title": "Minimum plate area", "type": "integer"}, "recognition_threshold": {"default": 0.9, "description": "Confidence threshold required for recognized plate text to be attached as a sub-label.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Recognition threshold", "type": "number"}, "min_plate_length": {"default": 4, "description": "Minimum number of characters a recognized plate must contain to be considered valid.", "title": "Min plate length", "type": "integer"}, "format": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional regex to validate recognized plate strings against an expected format.", "title": "Plate format regex"}, "match_distance": {"default": 1, "description": "Number of character mismatches allowed when comparing detected plates to known plates.", "minimum": 0, "title": "Match distance", "type": "integer"}, "known_plates": {"anyOf": [{"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "type": "object"}, {"type": "null"}], "default": {}, "description": "List of plates or regexes to specially track or alert on.", "title": "Known plates"}, "enhancement": {"default": 0, "description": "Enhancement level (0-10) to apply to plate crops prior to OCR; higher values may not always improve results, levels above 5 may only work with night time plates and should be used with caution.", "maximum": 10, "minimum": 0, "title": "Enhancement level", "type": "integer"}, "debug_save_plates": {"default": false, "description": "Save plate crop images for debugging LPR performance.", "title": "Save debug plates", "type": "boolean"}, "device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information", "title": "Device"}, "replace_rules": {"description": "Regex replacement rules used to normalize detected plate strings before matching.", "items": {"$ref": "#/$defs/ReplaceRule"}, "title": "Replacement rules", "type": "array"}}, "title": "LicensePlateRecognitionConfig", "type": "object"}, "ListenConfig": {"additionalProperties": false, "properties": {"internal": {"anyOf": [{"type": "integer"}, {"type": "string"}], "default": 5000, "description": "Internal listening port for Frigate (default 5000).", "title": "Internal port"}, "external": {"anyOf": [{"type": "integer"}, {"type": "string"}], "default": 8971, "description": "External listening port for Frigate (default 8971).", "title": "External port"}}, "title": "ListenConfig", "type": "object"}, "LogLevel": {"enum": ["debug", "info", "warning", "error", "critical"], "title": "LogLevel", "type": "string"}, "LoggerConfig": {"additionalProperties": false, "properties": {"default": {"$ref": "#/$defs/LogLevel", "default": "info", "title": "Logging level", "description": "Default global log verbosity (debug, info, warning, error)."}, "logs": {"additionalProperties": {"$ref": "#/$defs/LogLevel"}, "description": "Per-component log level overrides to increase or decrease verbosity for specific modules.", "title": "Per-process log level", "type": "object"}}, "title": "LoggerConfig", "type": "object"}, "ModelConfig": {"additionalProperties": false, "properties": {"path": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Path to a custom detection model file (or plus:// for Frigate+ models).", "title": "Custom object detector model path"}, "labelmap_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Path to a labelmap file that maps numeric classes to string labels for the detector.", "title": "Label map for custom object detector"}, "width": {"default": 320, "description": "Width of the model input tensor in pixels.", "title": "Object detection model input width", "type": "integer"}, "height": {"default": 320, "description": "Height of the model input tensor in pixels.", "title": "Object detection model input height", "type": "integer"}, "labelmap": {"additionalProperties": {"type": "string"}, "description": "Overrides or remapping entries to merge into the standard labelmap.", "title": "Labelmap customization", "type": "object"}, "attributes_map": {"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "default": {"person": ["amazon", "face"], "car": ["amazon", "an_post", "canada_post", "dhl", "dpd", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "motorcycle": ["license_plate"]}, "description": "Mapping from object labels to attribute labels used to attach metadata (for example 'car' -> ['license_plate']).", "title": "Map of object labels to their attribute labels", "type": "object"}, "input_tensor": {"$ref": "#/$defs/InputTensorEnum", "default": "nhwc", "description": "Tensor format expected by the model: 'nhwc' or 'nchw'.", "title": "Model Input Tensor Shape"}, "input_pixel_format": {"$ref": "#/$defs/PixelFormatEnum", "default": "rgb", "description": "Pixel colorspace expected by the model: 'rgb', 'bgr', or 'yuv'.", "title": "Model Input Pixel Color Format"}, "input_dtype": {"$ref": "#/$defs/InputDTypeEnum", "default": "int", "description": "Data type of the model input tensor (for example 'float32').", "title": "Model Input D Type"}, "model_type": {"$ref": "#/$defs/ModelTypeEnum", "default": "ssd", "description": "Detector model architecture type (ssd, yolox, yolonas, yolo-generic, rfdetr, dfine) used by some detectors for optimization.", "title": "Object Detection Model Type"}}, "title": "ModelConfig", "type": "object"}, "ModelSizeEnum": {"enum": ["small", "large"], "title": "ModelSizeEnum", "type": "string"}, "ModelTypeEnum": {"enum": ["dfine", "rfdetr", "ssd", "yolox", "yolonas", "yolo-generic"], "title": "ModelTypeEnum", "type": "string"}, "MotionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable motion detection for all cameras; can be overridden per-camera.", "title": "Enable motion detection", "type": "boolean"}, "threshold": {"default": 30, "description": "Pixel difference threshold used by the motion detector; higher values reduce sensitivity (range 1-255).", "maximum": 255, "minimum": 1, "title": "Motion threshold", "type": "integer"}, "lightning_threshold": {"default": 0.8, "description": "Threshold to detect and ignore brief lighting spikes (lower is more sensitive, values between 0.3 and 1.0). This does not prevent motion detection entirely; it merely causes the detector to stop analyzing additional frames once the threshold is exceeded. Motion-based recordings are still created during these events.", "maximum": 1.0, "minimum": 0.3, "title": "Lightning threshold", "type": "number"}, "skip_motion_threshold": {"anyOf": [{"maximum": 1.0, "minimum": 0.0, "type": "number"}, {"type": "null"}], "default": null, "description": "If set to a value between 0.0 and 1.0, and more than this fraction of the image changes in a single frame, the detector will return no motion boxes and immediately recalibrate. This can save CPU and reduce false positives during lightning, storms, etc., but may miss real events such as a PTZ camera auto\u2011tracking an object. The trade\u2011off is between dropping a few megabytes of recordings versus reviewing a couple short clips. Leave unset (None) to disable this feature.", "title": "Skip motion threshold"}, "improve_contrast": {"default": true, "description": "Apply contrast improvement to frames before motion analysis to help detection.", "title": "Improve contrast", "type": "boolean"}, "contour_area": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": 10, "description": "Minimum contour area in pixels required for a motion contour to be counted.", "title": "Contour area"}, "delta_alpha": {"default": 0.2, "description": "Alpha blending factor used in frame differencing for motion calculation.", "title": "Delta alpha", "type": "number"}, "frame_alpha": {"default": 0.01, "description": "Alpha value used when blending frames for motion preprocessing.", "title": "Frame alpha", "type": "number"}, "frame_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": 100, "description": "Height in pixels to scale frames to when computing motion.", "title": "Frame height"}, "mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/MotionMaskConfig"}, {"type": "null"}]}, "description": "Ordered x,y coordinates defining the motion mask polygon used to include/exclude areas.", "title": "Mask coordinates", "type": "object"}, "mqtt_off_delay": {"default": 30, "description": "Seconds to wait after last motion before publishing an MQTT 'off' state.", "title": "MQTT off delay", "type": "integer"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether motion detection was enabled in the original static configuration.", "title": "Original motion state"}, "raw_mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/MotionMaskConfig"}, {"type": "null"}]}, "title": "Raw Mask", "type": "object"}}, "title": "MotionConfig", "type": "object"}, "MotionMaskConfig": {"additionalProperties": false, "description": "Configuration for a single motion mask.", "properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "A friendly name for this motion mask used in the Frigate UI", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enable or disable this motion mask", "title": "Enabled", "type": "boolean"}, "coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "description": "Ordered x,y coordinates defining the motion mask polygon used to include/exclude areas.", "title": "Coordinates"}, "raw_coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "title": "Raw Coordinates"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Keep track of original state of motion mask."}}, "title": "MotionMaskConfig", "type": "object"}, "MqttConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable MQTT integration for state, events, and snapshots.", "title": "Enable MQTT", "type": "boolean"}, "host": {"default": "", "description": "Hostname or IP address of the MQTT broker.", "title": "MQTT host", "type": "string"}, "port": {"default": 1883, "description": "Port of the MQTT broker (usually 1883 for plain MQTT).", "title": "MQTT port", "type": "integer"}, "topic_prefix": {"default": "frigate", "description": "MQTT topic prefix for all Frigate topics; must be unique if running multiple instances.", "title": "Topic prefix", "type": "string"}, "client_id": {"default": "frigate", "description": "Client identifier used when connecting to the MQTT broker; should be unique per instance.", "title": "Client ID", "type": "string"}, "stats_interval": {"default": 60, "description": "Interval in seconds for publishing system and camera stats to MQTT.", "minimum": 15, "title": "Stats interval", "type": "integer"}, "user": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional MQTT username; can be provided via environment variables or secrets.", "title": "MQTT username"}, "password": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional MQTT password; can be provided via environment variables or secrets.", "title": "MQTT password"}, "tls_ca_certs": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Path to CA certificate for TLS connections to the broker (for self-signed certs).", "title": "TLS CA certs"}, "tls_client_cert": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Client certificate path for TLS mutual authentication; do not set user/password when using client certs.", "title": "Client cert"}, "tls_client_key": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Private key path for the client certificate.", "title": "Client key"}, "tls_insecure": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Allow insecure TLS connections by skipping hostname verification (not recommended).", "title": "TLS insecure"}, "qos": {"default": 0, "description": "Quality of Service level for MQTT publishes/subscriptions (0, 1, or 2).", "title": "MQTT QoS", "type": "integer"}}, "title": "MqttConfig", "type": "object"}, "NetworkingConfig": {"additionalProperties": false, "properties": {"ipv6": {"$ref": "#/$defs/IPv6Config", "description": "IPv6-specific settings for Frigate network services.", "title": "IPv6 configuration"}, "listen": {"$ref": "#/$defs/ListenConfig", "description": "Configuration for internal and external listening ports. This is for advanced users. For the majority of use cases it's recommended to change the ports section of your Docker compose file.", "title": "Listening ports configuration"}}, "title": "NetworkingConfig", "type": "object"}, "NotificationConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable notifications for all cameras; can be overridden per-camera.", "title": "Enable notifications", "type": "boolean"}, "email": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Email address used for push notifications or required by certain notification providers.", "title": "Notification email"}, "cooldown": {"default": 0, "description": "Cooldown (seconds) between notifications to avoid spamming recipients.", "minimum": 0, "title": "Cooldown period", "type": "integer"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether notifications were enabled in the original static configuration.", "title": "Original notifications state"}}, "title": "NotificationConfig", "type": "object"}, "ObjectClassificationType": {"enum": ["sub_label", "attribute"], "title": "ObjectClassificationType", "type": "string"}, "ObjectConfig": {"additionalProperties": false, "properties": {"track": {"default": ["person"], "description": "List of object labels to track for all cameras; can be overridden per-camera.", "items": {"type": "string"}, "title": "Objects to track", "type": "array"}, "filters": {"additionalProperties": {"$ref": "#/$defs/FilterConfig"}, "description": "Filters applied to detected objects to reduce false positives (area, ratio, confidence).", "title": "Object filters", "type": "object"}, "mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "description": "Mask polygon used to prevent object detection in specified areas.", "title": "Object mask", "type": "object"}, "raw_mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "title": "Raw Mask", "type": "object"}, "genai": {"$ref": "#/$defs/GenAIObjectConfig", "description": "GenAI options for describing tracked objects and sending frames for generation.", "title": "GenAI object config"}}, "title": "ObjectConfig", "type": "object"}, "ObjectMaskConfig": {"additionalProperties": false, "description": "Configuration for a single object mask.", "properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "A friendly name for this object mask used in the Frigate UI", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enable or disable this object mask", "title": "Enabled", "type": "boolean"}, "coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "description": "Ordered x,y coordinates defining the object mask polygon used to include/exclude areas.", "title": "Coordinates"}, "raw_coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "title": "Raw Coordinates"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Keep track of original state of object mask."}}, "title": "ObjectMaskConfig", "type": "object"}, "OnvifConfig": {"additionalProperties": false, "properties": {"host": {"default": "", "description": "Host (and optional scheme) for the ONVIF service for this camera.", "title": "ONVIF host", "type": "string"}, "port": {"default": 8000, "description": "Port number for the ONVIF service.", "title": "ONVIF port", "type": "integer"}, "user": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Username for ONVIF authentication; some devices require admin user for ONVIF.", "title": "ONVIF username"}, "password": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Password for ONVIF authentication.", "title": "ONVIF password"}, "tls_insecure": {"default": false, "description": "Skip TLS verification and disable digest auth for ONVIF (unsafe; use in safe networks only).", "title": "Disable TLS verify", "type": "boolean"}, "profile": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Specific ONVIF media profile to use for PTZ control, matched by token or name. If not set, the first profile with valid PTZ configuration is selected automatically.", "title": "ONVIF profile"}, "autotracking": {"$ref": "#/$defs/PtzAutotrackConfig", "description": "Automatically track moving objects and keep them centered in the frame using PTZ camera movements.", "title": "Autotracking"}, "ignore_time_mismatch": {"default": false, "description": "Ignore time synchronization differences between camera and Frigate server for ONVIF communication.", "title": "Ignore time mismatch", "type": "boolean"}}, "title": "OnvifConfig", "type": "object"}, "PixelFormatEnum": {"enum": ["rgb", "bgr", "yuv"], "title": "PixelFormatEnum", "type": "string"}, "ProfileDefinitionConfig": {"additionalProperties": false, "description": "Defines a named profile with a human-readable display name.\n\nThe dict key is the machine name used internally; friendly_name\nis the label shown in the UI and API responses.", "properties": {"friendly_name": {"description": "Display name for this profile shown in the UI.", "title": "Friendly name", "type": "string"}}, "required": ["friendly_name"], "title": "ProfileDefinitionConfig", "type": "object"}, "ProxyConfig": {"additionalProperties": false, "properties": {"header_map": {"$ref": "#/$defs/HeaderMappingConfig", "description": "Map incoming proxy headers to Frigate user and role fields for proxy-based auth.", "title": "Header mapping"}, "logout_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "URL to redirect users to when logging out via the proxy.", "title": "Logout URL"}, "auth_secret": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional secret checked against the X-Proxy-Secret header to verify trusted proxies.", "title": "Proxy secret"}, "default_role": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": "viewer", "description": "Default role assigned to proxy-authenticated users when no role mapping applies.", "title": "Default role"}, "separator": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": ",", "description": "Character used to split multiple values provided in proxy headers.", "title": "Separator character"}}, "title": "ProxyConfig", "type": "object"}, "PtzAutotrackConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable automatic PTZ camera tracking of detected objects.", "title": "Enable Autotracking", "type": "boolean"}, "calibrate_on_startup": {"default": false, "description": "Measure PTZ motor speeds on startup to improve tracking accuracy. Frigate will update config with movement_weights after calibration.", "title": "Calibrate on start", "type": "boolean"}, "zooming": {"$ref": "#/$defs/ZoomingModeEnum", "default": "disabled", "description": "Control zoom behavior: disabled (pan/tilt only), absolute (most compatible), or relative (concurrent pan/tilt/zoom).", "title": "Zoom mode"}, "zoom_factor": {"default": 0.3, "description": "Control zoom level on tracked objects. Lower values keep more scene in view; higher values zoom in closer but may lose tracking. Values between 0.1 and 0.75.", "maximum": 0.75, "minimum": 0.1, "title": "Zoom factor", "type": "number"}, "track": {"default": ["person"], "description": "List of object types that should trigger autotracking.", "items": {"type": "string"}, "title": "Tracked objects", "type": "array"}, "required_zones": {"description": "Objects must enter one of these zones before autotracking begins.", "items": {"type": "string"}, "title": "Required zones", "type": "array"}, "return_preset": {"default": "home", "description": "ONVIF preset name configured in camera firmware to return to after tracking ends.", "title": "Return preset", "type": "string"}, "timeout": {"default": 10, "description": "Wait this many seconds after losing tracking before returning camera to preset position.", "title": "Return timeout", "type": "integer"}, "movement_weights": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "description": "Calibration values automatically generated by camera calibration. Do not modify manually.", "title": "Movement weights"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Internal field to track whether autotracking was enabled in configuration.", "title": "Original autotrack state"}}, "title": "PtzAutotrackConfig", "type": "object"}, "RecordConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable recording for all cameras; can be overridden per-camera.", "title": "Enable recording", "type": "boolean"}, "expire_interval": {"default": 60, "description": "Minutes between cleanup passes that remove expired recording segments.", "title": "Record cleanup interval", "type": "integer"}, "continuous": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain recordings regardless of tracked objects or motion. Set to 0 if you only want to retain recordings of alerts and detections.", "title": "Continuous retention"}, "motion": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain recordings triggered by motion regardless of tracked objects. Set to 0 if you only want to retain recordings of alerts and detections.", "title": "Motion retention"}, "detections": {"$ref": "#/$defs/EventsConfig", "description": "Recording retention settings for detection events including pre/post capture durations.", "title": "Detection retention"}, "alerts": {"$ref": "#/$defs/EventsConfig", "description": "Recording retention settings for alert events including pre/post capture durations.", "title": "Alert retention"}, "export": {"$ref": "#/$defs/RecordExportConfig", "description": "Settings used when exporting recordings such as timelapse and hardware acceleration.", "title": "Export config"}, "preview": {"$ref": "#/$defs/RecordPreviewConfig", "description": "Settings controlling the quality of recording previews shown in the UI.", "title": "Preview config"}, "sub": {"$ref": "#/$defs/RecordSubConfig", "description": "Settings for recording a second, lower quality stream.", "title": "Sub stream recording"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether recording was enabled in the original static configuration.", "title": "Original recording state"}}, "title": "RecordConfig", "type": "object"}, "RecordExportConfig": {"additionalProperties": false, "properties": {"hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "auto", "description": "Hardware acceleration args to use for export/transcode operations.", "title": "Export hwaccel args"}, "max_concurrent": {"default": 3, "description": "Maximum number of export jobs to process at the same time.", "minimum": 1, "title": "Maximum concurrent exports", "type": "integer"}, "chapters": {"$ref": "#/$defs/ChaptersEnum", "default": "review_items", "title": "Chapter metadata to embed in exported recordings"}}, "title": "RecordExportConfig", "type": "object"}, "RecordPreviewConfig": {"additionalProperties": false, "properties": {"quality": {"$ref": "#/$defs/RecordQualityEnum", "default": "medium", "description": "Preview quality level (very_low, low, medium, high, very_high).", "title": "Preview quality"}}, "title": "RecordPreviewConfig", "type": "object"}, "RecordQualityEnum": {"enum": ["very_low", "low", "medium", "high", "very_high"], "title": "RecordQualityEnum", "type": "string"}, "RecordRetainConfig": {"additionalProperties": false, "properties": {"days": {"default": 0, "description": "Days to retain recordings.", "minimum": 0.0, "title": "Retention days", "type": "number"}}, "title": "RecordRetainConfig", "type": "object"}, "RecordSubConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable recording of a second, lower quality stream for adaptive quality playback and extended retention.", "title": "Enable sub stream recording", "type": "boolean"}, "continuous": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain sub stream recordings regardless of tracked objects or motion.", "title": "Sub stream continuous retention"}, "motion": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain sub stream recordings triggered by motion.", "title": "Sub stream motion retention"}, "alerts": {"$ref": "#/$defs/ReviewRetainConfig", "description": "Retention settings for sub stream recordings of alerts.", "title": "Sub stream alert retention"}, "detections": {"$ref": "#/$defs/ReviewRetainConfig", "description": "Retention settings for sub stream recordings of detections.", "title": "Sub stream detection retention"}}, "title": "RecordSubConfig", "type": "object"}, "ReplaceRule": {"additionalProperties": false, "properties": {"pattern": {"title": "Regex pattern", "type": "string"}, "replacement": {"title": "Replacement string", "type": "string"}}, "required": ["pattern", "replacement"], "title": "ReplaceRule", "type": "object"}, "RestreamConfig": {"additionalProperties": true, "properties": {}, "title": "RestreamConfig", "type": "object"}, "RetainConfig": {"additionalProperties": false, "properties": {"default": {"type": "number", "default": 10, "title": "Default retention", "description": "Default number of days to retain snapshots."}, "objects": {"additionalProperties": {"type": "number"}, "description": "Per-object overrides for snapshot retention days.", "title": "Object retention", "type": "object"}}, "title": "RetainConfig", "type": "object"}, "RetainModeEnum": {"enum": ["all", "motion", "active_objects"], "title": "RetainModeEnum", "type": "string"}, "ReviewConfig": {"additionalProperties": false, "properties": {"alerts": {"$ref": "#/$defs/AlertsConfig", "description": "Settings for which tracked objects generate alerts and how alerts are retained.", "title": "Alerts config"}, "detections": {"$ref": "#/$defs/DetectionsConfig", "description": "Settings for which tracked objects generate detections (non-alert) and how detections are retained.", "title": "Detections config"}, "genai": {"$ref": "#/$defs/GenAIReviewConfig", "description": "Controls use of generative AI for producing descriptions and summaries of review items.", "title": "GenAI config"}}, "title": "ReviewConfig", "type": "object"}, "ReviewRetainConfig": {"additionalProperties": false, "properties": {"days": {"default": 10, "description": "Number of days to retain recordings of detection events.", "minimum": 0.0, "title": "Retention days", "type": "number"}, "mode": {"$ref": "#/$defs/RetainModeEnum", "default": "motion", "description": "Mode for retention: all (save all segments), motion (save segments with motion), or active_objects (save segments with active objects).", "title": "Retention mode"}}, "title": "ReviewRetainConfig", "type": "object"}, "SemanticSearchConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable the semantic search feature.", "title": "Enable semantic search", "type": "boolean"}, "reindex": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "Trigger a full reindex of historical tracked objects into the embeddings database.", "title": "Reindex on startup"}, "model": {"anyOf": [{"$ref": "#/$defs/SemanticSearchModelEnum"}, {"type": "string"}, {"type": "null"}], "default": "jinav1", "description": "The embeddings model to use for semantic search (for example 'jinav1'), or the name of a GenAI provider with the embeddings role.", "title": "Semantic search model or GenAI provider name"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Select model size; 'small' runs on CPU and 'large' typically requires GPU.", "title": "Model size"}, "device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information", "title": "Device"}}, "title": "SemanticSearchConfig", "type": "object"}, "SemanticSearchModelEnum": {"enum": ["jinav1", "jinav2"], "title": "SemanticSearchModelEnum", "type": "string"}, "SnapshotsConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable saving snapshots for all cameras; can be overridden per-camera.", "title": "Enable snapshots", "type": "boolean"}, "timestamp": {"default": false, "description": "Overlay a timestamp on snapshots from API.", "title": "Timestamp overlay", "type": "boolean"}, "bounding_box": {"default": true, "description": "Draw bounding boxes for tracked objects on snapshots from API.", "title": "Bounding box overlay", "type": "boolean"}, "crop": {"default": false, "description": "Crop snapshots from API to the detected object's bounding box.", "title": "Crop snapshot", "type": "boolean"}, "required_zones": {"description": "Zones an object must enter for a snapshot to be saved.", "items": {"type": "string"}, "title": "Required zones", "type": "array"}, "height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Height (pixels) to resize snapshots from API to; leave empty to preserve original size.", "title": "Snapshot height"}, "retain": {"$ref": "#/$defs/RetainConfig", "description": "Retention settings for snapshots including default days and per-object overrides.", "title": "Snapshot retention"}, "quality": {"default": 60, "description": "Encode quality for saved snapshots (0-100).", "maximum": 100, "minimum": 0, "title": "Snapshot quality", "type": "integer"}}, "title": "SnapshotsConfig", "type": "object"}, "StationaryConfig": {"additionalProperties": false, "properties": {"interval": {"anyOf": [{"exclusiveMinimum": 0, "type": "integer"}, {"type": "null"}], "default": null, "description": "How often (in frames) to run a detection check to confirm a stationary object.", "title": "Stationary interval"}, "threshold": {"anyOf": [{"minimum": 1, "type": "integer"}, {"type": "null"}], "default": null, "description": "Number of frames with no position change required to mark an object as stationary.", "title": "Stationary threshold"}, "max_frames": {"$ref": "#/$defs/StationaryMaxFramesConfig", "description": "Limits how long stationary objects are tracked before being discarded.", "title": "Max frames"}, "classifier": {"default": true, "description": "Use a visual classifier to detect truly stationary objects even when bounding boxes jitter.", "title": "Enable visual classifier", "type": "boolean"}}, "title": "StationaryConfig", "type": "object"}, "StationaryMaxFramesConfig": {"additionalProperties": false, "properties": {"default": {"anyOf": [{"minimum": 1, "type": "integer"}, {"type": "null"}], "default": null, "title": "Default max frames", "description": "Default maximum frames to track a stationary object before stopping."}, "objects": {"additionalProperties": {"type": "integer"}, "description": "Per-object overrides for maximum frames to track stationary objects.", "title": "Object max frames", "type": "object"}}, "title": "StationaryMaxFramesConfig", "type": "object"}, "StatsConfig": {"additionalProperties": false, "properties": {"amd_gpu_stats": {"default": true, "description": "Enable collection of AMD GPU statistics if an AMD GPU is present.", "title": "AMD GPU stats", "type": "boolean"}, "intel_gpu_stats": {"default": true, "description": "Enable collection of Intel GPU statistics if an Intel GPU is present.", "title": "Intel GPU stats", "type": "boolean"}, "network_bandwidth": {"default": false, "description": "Enable per-process network bandwidth monitoring for camera ffmpeg processes and detectors (requires capabilities).", "title": "Network bandwidth", "type": "boolean"}, "intel_gpu_device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "PCI bus address or DRM device path (e.g. /dev/dri/card1) used to pin Intel GPU stats to a specific device when multiple are present.", "title": "Intel GPU device"}}, "title": "StatsConfig", "type": "object"}, "TelemetryConfig": {"additionalProperties": false, "properties": {"network_interfaces": {"default": [], "description": "List of network interface name prefixes to monitor for bandwidth statistics.", "items": {"type": "string"}, "title": "Network interfaces", "type": "array"}, "stats": {"$ref": "#/$defs/StatsConfig", "description": "Options to enable/disable collection of various system and GPU statistics.", "title": "System stats"}, "version_check": {"default": true, "description": "Enable an outbound check to detect if a newer Frigate version is available.", "title": "Version check", "type": "boolean"}}, "title": "TelemetryConfig", "type": "object"}, "TimeFormatEnum": {"enum": ["browser", "12hour", "24hour"], "title": "TimeFormatEnum", "type": "string"}, "TimestampEffectEnum": {"enum": ["solid", "shadow"], "title": "TimestampEffectEnum", "type": "string"}, "TimestampPositionEnum": {"enum": ["tl", "tr", "bl", "br"], "title": "TimestampPositionEnum", "type": "string"}, "TimestampStyleConfig": {"additionalProperties": false, "properties": {"position": {"$ref": "#/$defs/TimestampPositionEnum", "default": "tl", "description": "Position of the timestamp on the image (tl/tr/bl/br).", "title": "Timestamp position"}, "format": {"default": "%m/%d/%Y %H:%M:%S", "description": "Datetime format string used for timestamps (Python datetime format codes).", "title": "Timestamp format", "type": "string"}, "color": {"$ref": "#/$defs/ColorConfig", "description": "RGB color values for the timestamp text (all values 0-255).", "title": "Timestamp color"}, "thickness": {"default": 2, "description": "Line thickness of the timestamp text.", "title": "Timestamp thickness", "type": "integer"}, "effect": {"anyOf": [{"$ref": "#/$defs/TimestampEffectEnum"}, {"type": "null"}], "default": null, "description": "Visual effect for the timestamp text (none, solid, shadow).", "title": "Timestamp effect"}}, "title": "TimestampStyleConfig", "type": "object"}, "TlsConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable TLS for Frigate's web UI and API on the configured TLS port.", "title": "Enable TLS", "type": "boolean"}}, "title": "TlsConfig", "type": "object"}, "TriggerAction": {"enum": ["notification", "sub_label", "attribute"], "title": "TriggerAction", "type": "string"}, "TriggerConfig": {"additionalProperties": false, "properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional friendly name displayed in the UI for this trigger.", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enable or disable this semantic search trigger.", "title": "Enable this trigger", "type": "boolean"}, "type": {"$ref": "#/$defs/TriggerType", "default": "description", "description": "Type of trigger: 'thumbnail' (match against image) or 'description' (match against text).", "title": "Trigger type"}, "data": {"description": "Text phrase or thumbnail ID to match against tracked objects.", "title": "Trigger content", "type": "string"}, "threshold": {"default": 0.8, "description": "Minimum similarity score (0-1) required to activate this trigger.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Trigger threshold", "type": "number"}, "actions": {"default": [], "description": "List of actions to execute when trigger matches (notification, sub_label, attribute).", "items": {"$ref": "#/$defs/TriggerAction"}, "title": "Trigger actions", "type": "array"}}, "required": ["data"], "title": "TriggerConfig", "type": "object"}, "TriggerType": {"enum": ["thumbnail", "description"], "title": "TriggerType", "type": "string"}, "UIConfig": {"additionalProperties": false, "properties": {"timezone": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional timezone to display across the UI (defaults to browser local time if unset).", "title": "Timezone"}, "time_format": {"$ref": "#/$defs/TimeFormatEnum", "default": "browser", "description": "Time format to use in the UI (browser, 12hour, or 24hour).", "title": "Time format"}, "unit_system": {"$ref": "#/$defs/UnitSystemEnum", "default": "metric", "description": "Unit system for display (metric or imperial) used in the UI and MQTT.", "title": "Unit system"}}, "title": "UIConfig", "type": "object"}, "UnitSystemEnum": {"enum": ["imperial", "metric"], "title": "UnitSystemEnum", "type": "string"}, "ZoneConfig": {"properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "A user-friendly name for the zone, displayed in the Frigate UI. If not set, a formatted version of the zone name will be used.", "title": "Zone name"}, "enabled": {"default": true, "description": "Enable or disable this zone. Disabled zones are ignored at runtime.", "title": "Enabled", "type": "boolean"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Keep track of original state of zone."}, "filters": {"additionalProperties": {"$ref": "#/$defs/FilterConfig"}, "description": "Filters to apply to objects within this zone. Used to reduce false positives or restrict which objects are considered present in the zone.", "title": "Zone filters", "type": "object"}, "coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Polygon coordinates that define the zone area. Can be a comma-separated string or a list of coordinate strings. Coordinates should be relative (0-1) or absolute (legacy).", "title": "Coordinates"}, "distances": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "description": "Optional real-world distances for each side of the zone quadrilateral, used for speed or distance calculations. Must have exactly 4 values if set.", "title": "Real-world distances"}, "inertia": {"default": 3, "description": "Number of consecutive frames an object must be detected in the zone before it is considered present. Helps filter out transient detections.", "exclusiveMinimum": 0, "title": "Inertia frames", "type": "integer"}, "loitering_time": {"default": 0, "description": "Number of seconds an object must remain in the zone to be considered as loitering. Set to 0 to disable loitering detection.", "minimum": 0, "title": "Loitering seconds", "type": "integer"}, "speed_threshold": {"anyOf": [{"minimum": 0.1, "type": "number"}, {"type": "null"}], "default": null, "description": "Minimum speed (in real-world units if distances are set) required for an object to be considered present in the zone. Used for speed-based zone triggers.", "title": "Minimum speed"}, "objects": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "List of object types (from labelmap) that can trigger this zone. Can be a string or a list of strings. If empty, all objects are considered.", "title": "Trigger objects"}}, "required": ["coordinates"], "title": "ZoneConfig", "type": "object"}, "ZoomingModeEnum": {"enum": ["disabled", "absolute", "relative"], "title": "ZoomingModeEnum", "type": "string"}}, "additionalProperties": false, "properties": {"version": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Numeric or string version of the active configuration to help detect migrations or format changes.", "title": "Current config version"}, "safe_mode": {"default": false, "description": "When enabled, start Frigate in safe mode with reduced features for troubleshooting.", "title": "Safe mode", "type": "boolean"}, "environment_vars": {"additionalProperties": {"type": "string"}, "description": "Key/value pairs of environment variables to set for the Frigate process in Home Assistant OS. Non-HAOS users must use Docker environment variable configuration instead.", "title": "Environment variables", "type": "object"}, "logger": {"$ref": "#/$defs/LoggerConfig", "description": "Controls default log verbosity and per-component log level overrides.", "title": "Logging"}, "auth": {"$ref": "#/$defs/AuthConfig", "description": "Authentication and session-related settings including cookie and rate limit options.", "title": "Authentication"}, "database": {"$ref": "#/$defs/DatabaseConfig", "description": "Settings for the SQLite database used by Frigate to store tracked object and recording metadata.", "title": "Database"}, "go2rtc": {"$ref": "#/$defs/RestreamConfig", "description": "Settings for the integrated go2rtc restreaming service used for live stream relaying and translation.", "title": "go2rtc"}, "mqtt": {"$ref": "#/$defs/MqttConfig", "description": "Settings for connecting and publishing telemetry, snapshots, and event details to an MQTT broker.", "title": "MQTT"}, "notifications": {"$ref": "#/$defs/NotificationConfig", "description": "Settings to enable and control notifications for all cameras; can be overridden per-camera.", "title": "Notifications"}, "networking": {"$ref": "#/$defs/NetworkingConfig", "description": "Network-related settings such as IPv6 enablement for Frigate endpoints.", "title": "Networking"}, "proxy": {"$ref": "#/$defs/ProxyConfig", "description": "Settings for integrating Frigate behind a reverse proxy that passes authenticated user headers.", "title": "Proxy"}, "telemetry": {"$ref": "#/$defs/TelemetryConfig", "description": "System telemetry and stats options including GPU and network bandwidth monitoring.", "title": "Telemetry"}, "tls": {"$ref": "#/$defs/TlsConfig", "description": "TLS settings for Frigate's web endpoints (port 8971).", "title": "TLS"}, "ui": {"$ref": "#/$defs/UIConfig", "description": "User interface preferences such as timezone, time/date formatting, and units.", "title": "UI"}, "detectors": {"additionalProperties": {"$ref": "#/$defs/BaseDetectorConfig"}, "default": {"cpu": {"type": "cpu"}}, "description": "Configuration for object detectors (CPU, GPU, ONNX backends) and any detector-specific model settings.", "title": "Detector hardware", "type": "object"}, "model": {"$ref": "#/$defs/ModelConfig", "description": "Settings to configure a custom object detection model and its input shape.", "title": "Detection model"}, "genai": {"additionalProperties": {"$ref": "#/$defs/GenAIConfig"}, "description": "Settings for integrated generative AI providers used to generate object descriptions and review summaries.", "title": "Generative AI configuration", "type": "object"}, "cameras": {"additionalProperties": {"$ref": "#/$defs/CameraConfig"}, "description": "Cameras", "title": "Cameras", "type": "object"}, "audio": {"$ref": "#/$defs/AudioConfig", "description": "Settings for audio-based event detection for all cameras; can be overridden per-camera.", "title": "Audio detection"}, "birdseye": {"$ref": "#/$defs/BirdseyeConfig", "description": "Settings for the Birdseye composite view that composes multiple camera feeds into a single layout.", "title": "Birdseye"}, "detect": {"$ref": "#/$defs/DetectConfig", "description": "Settings for the detection/detect role used to run object detection and initialize trackers.", "title": "Object Detection"}, "ffmpeg": {"$ref": "#/$defs/FfmpegConfig", "description": "FFmpeg settings including binary path, args, hwaccel options, and per-role output args.", "title": "FFmpeg"}, "live": {"$ref": "#/$defs/CameraLiveConfig", "description": "Settings to control the jsmpeg live stream resolution and quality. This does not affect restreamed cameras that use go2rtc for live view.", "title": "Live playback"}, "motion": {"anyOf": [{"$ref": "#/$defs/MotionConfig"}, {"type": "null"}], "default": null, "description": "Default motion detection settings applied to cameras unless overridden per-camera.", "title": "Motion detection"}, "objects": {"$ref": "#/$defs/ObjectConfig", "description": "Object tracking defaults including which labels to track and per-object filters.", "title": "Objects"}, "record": {"$ref": "#/$defs/RecordConfig", "description": "Recording and retention settings applied to cameras unless overridden per-camera.", "title": "Recording"}, "review": {"$ref": "#/$defs/ReviewConfig", "description": "Settings that control alerts, detections, and GenAI review summaries used by the UI and storage.", "title": "Review"}, "snapshots": {"$ref": "#/$defs/SnapshotsConfig", "description": "Settings for API-generated snapshots of tracked objects for all cameras; can be overridden per-camera.", "title": "Snapshots"}, "timestamp_style": {"$ref": "#/$defs/TimestampStyleConfig", "description": "Styling options for in-feed timestamps applied to debug view and snapshots.", "title": "Timestamp style"}, "audio_transcription": {"$ref": "#/$defs/AudioTranscriptionConfig", "description": "Settings for live and speech audio transcription used for events and live captions.", "title": "Audio transcription"}, "classification": {"$ref": "#/$defs/ClassificationConfig", "description": "Settings for classification models used to refine object labels or state classification.", "title": "Object classification"}, "semantic_search": {"$ref": "#/$defs/SemanticSearchConfig", "description": "Settings for Semantic Search which builds and queries object embeddings to find similar items.", "title": "Semantic Search"}, "face_recognition": {"$ref": "#/$defs/FaceRecognitionConfig", "description": "Settings for face detection and recognition for all cameras; can be overridden per-camera.", "title": "Face recognition"}, "lpr": {"$ref": "#/$defs/LicensePlateRecognitionConfig", "description": "License plate recognition settings including detection thresholds, formatting, and known plates.", "title": "License Plate Recognition"}, "camera_groups": {"additionalProperties": {"$ref": "#/$defs/CameraGroupConfig"}, "description": "Configuration for named camera groups used to organize cameras in the UI.", "title": "Camera groups", "type": "object"}, "profiles": {"additionalProperties": {"$ref": "#/$defs/ProfileDefinitionConfig"}, "description": "Named profile definitions with friendly names. Camera profiles must reference names defined here.", "title": "Profiles", "type": "object"}, "active_profile": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Currently active profile name. Runtime-only, not persisted in YAML.", "title": "Active profile"}}, "required": ["mqtt", "cameras"], "title": "FrigateConfig", "type": "object"} \ No newline at end of file +{"$defs": {"AlertsConfig": {"additionalProperties": false, "description": "Configure alerts", "properties": {"enabled": {"default": true, "description": "Enable or disable alert generation for all cameras; can be overridden per-camera.", "title": "Enable alerts", "type": "boolean"}, "labels": {"default": ["person", "car"], "description": "List of object labels that qualify as alerts (for example: car, person).", "items": {"type": "string"}, "title": "Alert labels", "type": "array"}, "required_zones": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Zones that an object must enter to be considered an alert; leave empty to allow any zone.", "title": "Required zones"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Tracks whether alerts were originally enabled in the static configuration.", "title": "Original alerts state"}, "cutoff_time": {"default": 40, "description": "Seconds to wait after no alert-causing activity before cutting off an alert.", "title": "Alerts cutoff time", "type": "integer"}}, "title": "AlertsConfig", "type": "object"}, "AudioConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable audio event detection for all cameras; can be overridden per-camera.", "title": "Enable audio detection", "type": "boolean"}, "max_not_heard": {"default": 30, "description": "Amount of seconds without the configured audio type before the audio event is ended.", "title": "End timeout", "type": "integer"}, "min_volume": {"default": 500, "description": "Minimum RMS volume threshold required to run audio detection; lower values increase sensitivity (e.g., 200 high, 500 medium, 1000 low).", "title": "Minimum volume", "type": "integer"}, "listen": {"default": ["bark", "fire_alarm", "speech", "yell"], "description": "List of audio event types to detect (for example: bark, fire_alarm, speech, yell).", "items": {"type": "string"}, "title": "Listen types", "type": "array"}, "filters": {"anyOf": [{"additionalProperties": {"$ref": "#/$defs/AudioFilterConfig"}, "type": "object"}, {"type": "null"}], "default": null, "description": "Per-audio-type filter settings such as confidence thresholds used to reduce false positives.", "title": "Audio filters"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether audio detection was originally enabled in the static config file.", "title": "Original audio state"}, "num_threads": {"default": 2, "description": "Number of threads to use for audio detection processing.", "minimum": 1, "title": "Detection threads", "type": "integer"}}, "title": "AudioConfig", "type": "object"}, "AudioFilterConfig": {"additionalProperties": false, "properties": {"threshold": {"default": 0.8, "description": "Minimum confidence threshold for the audio event to be counted.", "exclusiveMaximum": 1.0, "minimum": 0.5, "title": "Minimum audio confidence", "type": "number"}}, "title": "AudioFilterConfig", "type": "object"}, "AudioTranscriptionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable automatic audio transcription for all cameras; can be overridden per-camera.", "title": "Enable audio transcription", "type": "boolean"}, "language": {"default": "en", "description": "Language code used for transcription/translation (for example 'en' for English). See https://whisper-api.com/docs/languages/ for supported language codes.", "title": "Transcription language", "type": "string"}, "device": {"$ref": "#/$defs/EnrichmentsDeviceEnum", "default": "CPU", "description": "Device key (CPU/GPU) to run the transcription model on. Only NVIDIA CUDA GPUs are currently supported for transcription.", "title": "Transcription device"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Model size to use for offline audio event transcription.", "title": "Model size"}, "live_enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "Enable streaming live transcription for audio as it is received.", "title": "Live transcription"}}, "title": "AudioTranscriptionConfig", "type": "object"}, "AuthConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable native authentication for the Frigate UI.", "title": "Enable authentication", "type": "boolean"}, "reset_admin_password": {"default": false, "description": "If true, reset the admin user's password on startup and print the new password in logs.", "title": "Reset admin password", "type": "boolean"}, "cookie_name": {"default": "frigate_token", "description": "Name of the cookie used to store the JWT token for native authentication.", "pattern": "^[a-z_]+$", "title": "JWT cookie name", "type": "string"}, "cookie_secure": {"default": false, "description": "Set the secure flag on the auth cookie; should be true when using TLS.", "title": "Secure cookie flag", "type": "boolean"}, "session_length": {"default": 86400, "description": "Session duration in seconds for JWT-based sessions.", "minimum": 60, "title": "Session length", "type": "integer"}, "refresh_time": {"default": 1800, "description": "When a session is within this many seconds of expiring, refresh it back to full length.", "minimum": 30, "title": "Session refresh window", "type": "integer"}, "failed_login_rate_limit": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Rate limiting rules for failed login attempts to reduce brute-force attacks.", "title": "Failed login limits"}, "trusted_proxies": {"default": [], "description": "List of trusted proxy IPs used when determining client IP for rate limiting.", "items": {"type": "string"}, "title": "Trusted proxies", "type": "array"}, "hash_iterations": {"default": 600000, "description": "Number of PBKDF2-SHA256 iterations to use when hashing user passwords.", "title": "Hash iterations", "type": "integer"}, "roles": {"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "description": "Map roles to camera lists. An empty list grants access to all cameras for the role.", "title": "Role mappings", "type": "object"}, "admin_first_time_login": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "When true the UI may show a help link on the login page informing users how to sign in after an admin password reset. ", "title": "First-time admin flag"}}, "title": "AuthConfig", "type": "object"}, "BaseDetectorConfig": {"additionalProperties": true, "properties": {"type": {"default": "cpu", "description": "Type of detector to use for object detection (for example 'cpu', 'edgetpu', 'openvino').", "title": "Detector Type", "type": "string"}, "model": {"anyOf": [{"$ref": "#/$defs/ModelConfig"}, {"type": "null"}], "default": null, "description": "Detector-specific model configuration options (path, input size, etc.).", "title": "Detector specific model configuration"}, "model_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "File path to the detector model binary if required by the chosen detector.", "title": "Detector specific model path"}}, "title": "BaseDetectorConfig", "type": "object"}, "BirdClassificationConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable bird classification.", "title": "Bird classification", "type": "boolean"}, "threshold": {"default": 0.9, "description": "Minimum classification score required to accept a bird classification.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Minimum score", "type": "number"}}, "title": "BirdClassificationConfig", "type": "object"}, "BirdseyeCameraConfig": {"properties": {"enabled": {"default": true, "description": "Enable or disable the Birdseye view feature.", "title": "Enable Birdseye", "type": "boolean"}, "modes": {"description": "Activity types that include cameras in Birdseye.", "items": {"$ref": "#/$defs/BirdseyeModeEnum"}, "title": "Activity types", "type": "array"}, "order": {"default": 0, "description": "Numeric position controlling the camera's ordering in the Birdseye layout.", "title": "Position", "type": "integer"}}, "title": "BirdseyeCameraConfig", "type": "object"}, "BirdseyeConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable the Birdseye view feature.", "title": "Enable Birdseye", "type": "boolean"}, "modes": {"description": "Activity types that include cameras in Birdseye.", "items": {"$ref": "#/$defs/BirdseyeModeEnum"}, "title": "Activity types", "type": "array"}, "restream": {"default": false, "description": "Re-stream the Birdseye output as an RTSP feed; enabling this will keep Birdseye running continuously.", "title": "Restream RTSP", "type": "boolean"}, "width": {"default": 1280, "description": "Output width (pixels) of the composed Birdseye frame.", "title": "Width", "type": "integer"}, "height": {"default": 720, "description": "Output height (pixels) of the composed Birdseye frame.", "title": "Height", "type": "integer"}, "quality": {"default": 8, "description": "Encoding quality for the Birdseye mpeg1 feed (1 highest quality, 31 lowest).", "maximum": 31, "minimum": 1, "title": "Encoding quality", "type": "integer"}, "inactivity_threshold": {"default": 30, "description": "Seconds of inactivity after which a camera will stop being shown in Birdseye.", "exclusiveMinimum": 0, "title": "Inactivity threshold", "type": "integer"}, "layout": {"$ref": "#/$defs/BirdseyeLayoutConfig", "description": "Layout options for the Birdseye composition.", "title": "Layout"}, "idle_heartbeat_fps": {"default": 0.0, "description": "Frames-per-second to resend the last composed Birdseye frame when idle; set to 0 to disable.", "maximum": 10.0, "minimum": 0.0, "title": "Idle heartbeat FPS", "type": "number"}}, "title": "BirdseyeConfig", "type": "object"}, "BirdseyeLayoutConfig": {"additionalProperties": false, "properties": {"scaling_factor": {"default": 2.0, "description": "Scaling factor used by the layout calculator (range 1.0 to 5.0).", "maximum": 5.0, "minimum": 1.0, "title": "Scaling factor", "type": "number"}, "max_cameras": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Maximum number of cameras to display at once in Birdseye; shows the most recent cameras.", "title": "Max cameras"}}, "title": "BirdseyeLayoutConfig", "type": "object"}, "BirdseyeModeEnum": {"enum": ["continuous", "motion", "all_objects", "alerts", "detections"], "title": "BirdseyeModeEnum", "type": "string"}, "CameraAudioTranscriptionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable manually triggered audio event transcription.", "title": "Enable transcription", "type": "boolean"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Original transcription state"}, "live_enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "Enable streaming live transcription for audio as it is received.", "title": "Live transcription"}}, "title": "CameraAudioTranscriptionConfig", "type": "object"}, "CameraConfig": {"additionalProperties": false, "properties": {"name": {"anyOf": [{"pattern": "^[a-zA-Z0-9_-]+$", "type": "string"}, {"type": "null"}], "default": null, "description": "Camera name is required", "title": "Camera name"}, "friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Camera friendly name used in the Frigate UI", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enabled", "title": "Enabled", "type": "boolean"}, "audio": {"$ref": "#/$defs/AudioConfig", "description": "Settings for audio-based event detection for this camera.", "title": "Audio detection"}, "audio_transcription": {"$ref": "#/$defs/CameraAudioTranscriptionConfig", "description": "Settings for live and speech audio transcription used for events and live captions.", "title": "Audio transcription"}, "birdseye": {"$ref": "#/$defs/BirdseyeCameraConfig", "description": "Settings for the Birdseye composite view that composes multiple camera feeds into a single layout.", "title": "Birdseye"}, "detect": {"$ref": "#/$defs/DetectConfig", "description": "Settings for the detection/detect role used to run object detection and initialize trackers.", "title": "Object Detection"}, "face_recognition": {"$ref": "#/$defs/CameraFaceRecognitionConfig", "description": "Settings for face detection and recognition for this camera.", "title": "Face recognition"}, "ffmpeg": {"$ref": "#/$defs/CameraFfmpegConfig", "description": "Camera stream inputs and FFmpeg options, including binary path, args, hwaccel, and per-role output args.", "title": "Streams (FFmpeg)"}, "live": {"$ref": "#/$defs/CameraLiveConfig", "description": "Settings used by the Web UI to control live stream selection, resolution and quality.", "title": "Live playback"}, "lpr": {"$ref": "#/$defs/CameraLicensePlateRecognitionConfig", "description": "License plate recognition settings including detection thresholds, formatting, and known plates.", "title": "License Plate Recognition"}, "motion": {"$ref": "#/$defs/MotionConfig", "default": null, "description": "Default motion detection settings for this camera.", "title": "Motion detection"}, "objects": {"$ref": "#/$defs/ObjectConfig", "description": "Object tracking defaults including which labels to track and per-object filters.", "title": "Objects"}, "record": {"$ref": "#/$defs/RecordConfig", "description": "Recording and retention settings for this camera.", "title": "Recording"}, "review": {"$ref": "#/$defs/ReviewConfig", "description": "Settings that control alerts, detections, and GenAI review summaries used by the UI and storage for this camera.", "title": "Review"}, "semantic_search": {"$ref": "#/$defs/CameraSemanticSearchConfig", "description": "Settings for semantic search which builds and queries object embeddings to find similar items.", "title": "Semantic Search"}, "snapshots": {"$ref": "#/$defs/SnapshotsConfig", "description": "Settings for API-generated snapshots of tracked objects for this camera.", "title": "Snapshots"}, "timestamp_style": {"$ref": "#/$defs/TimestampStyleConfig", "description": "Styling options for timestamps applied to snapshots and Debug view.", "title": "Timestamp style"}, "best_image_timeout": {"default": 60, "description": "How long to wait for the image with the highest confidence score.", "title": "Best image timeout", "type": "integer"}, "mqtt": {"$ref": "#/$defs/CameraMqttConfig", "description": "MQTT image publishing settings.", "title": "MQTT"}, "notifications": {"$ref": "#/$defs/NotificationConfig", "description": "Settings to enable and control notifications for this camera.", "title": "Notifications"}, "onvif": {"$ref": "#/$defs/OnvifConfig", "description": "ONVIF connection and PTZ autotracking settings for this camera.", "title": "ONVIF"}, "type": {"$ref": "#/$defs/CameraTypeEnum", "default": "generic", "description": "Camera Type", "title": "Camera type"}, "ui": {"$ref": "#/$defs/CameraUiConfig", "description": "Display ordering and visibility for this camera in the UI. Ordering affects the default dashboard. For more granular control, use camera groups.", "title": "Camera UI"}, "webui_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "URL to visit the camera directly from system page", "title": "Camera URL"}, "profiles": {"additionalProperties": {"$ref": "#/$defs/CameraProfileConfig"}, "description": "Named config profiles with partial overrides that can be activated at runtime.", "title": "Profiles", "type": "object"}, "zones": {"additionalProperties": {"$ref": "#/$defs/ZoneConfig"}, "description": "Zones allow you to define a specific area of the frame so you can determine whether or not an object is within a particular area.", "title": "Zones", "type": "object"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Keep track of original state of camera.", "title": "Original camera state"}}, "required": ["ffmpeg"], "title": "CameraConfig", "type": "object"}, "CameraFaceRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable face recognition.", "title": "Enable face recognition", "type": "boolean"}, "min_area": {"default": 750, "description": "Minimum area (pixels) of a detected face box required to attempt recognition.", "title": "Minimum face area", "type": "integer"}}, "title": "CameraFaceRecognitionConfig", "type": "object"}, "CameraFfmpegConfig": {"additionalProperties": false, "properties": {"path": {"default": "default", "description": "Path to the FFmpeg binary to use or a version alias (\"7.0\" or \"8.0\").", "title": "FFmpeg path", "type": "string"}, "global_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "description": "Global arguments passed to FFmpeg processes.", "title": "FFmpeg global arguments"}, "hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "auto", "description": "Hardware acceleration arguments for FFmpeg. Provider-specific presets are recommended.", "title": "Hardware acceleration arguments"}, "input_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "preset-rtsp-generic", "description": "Input arguments applied to FFmpeg input streams.", "title": "Input arguments"}, "output_args": {"$ref": "#/$defs/FfmpegOutputArgsConfig", "description": "Default output arguments used for different FFmpeg roles such as detect and record.", "title": "Output arguments"}, "retry_interval": {"default": 10.0, "description": "Seconds to wait before attempting to reconnect a camera stream after failure. Default is 10.", "exclusiveMinimum": 0.0, "title": "FFmpeg retry time", "type": "number"}, "apple_compatibility": {"default": false, "description": "Enable HEVC tagging for better Apple player compatibility when recording H.265.", "title": "Apple compatibility", "type": "boolean"}, "gpu": {"default": 0, "description": "Default GPU index used for hardware acceleration if available.", "title": "GPU index", "type": "integer"}, "inputs": {"description": "List of input stream definitions (paths and roles) for this camera.", "items": {"$ref": "#/$defs/CameraInput"}, "title": "Camera inputs", "type": "array"}}, "required": ["inputs"], "title": "CameraFfmpegConfig", "type": "object"}, "CameraGroupConfig": {"additionalProperties": false, "properties": {"cameras": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Array of camera names included in this group.", "title": "Camera list"}, "icon": {"default": "generic", "description": "Icon used to represent the camera group in the UI.", "title": "Group icon", "type": "string"}, "order": {"default": 0, "description": "Numeric order used to sort camera groups in the UI; larger numbers appear later.", "title": "Sort order", "type": "integer"}}, "title": "CameraGroupConfig", "type": "object"}, "CameraInput": {"additionalProperties": false, "properties": {"path": {"description": "Camera input stream URL or path.", "title": "Input path", "type": "string"}, "roles": {"description": "Roles for this input stream.", "items": {"$ref": "#/$defs/CameraRoleEnum"}, "title": "Input roles", "type": "array"}, "global_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "FFmpeg global arguments for this input stream.", "title": "FFmpeg global arguments"}, "hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Hardware acceleration arguments for this input stream.", "title": "Hardware acceleration arguments"}, "input_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Input arguments specific to this stream.", "title": "Input arguments"}}, "required": ["path", "roles"], "title": "CameraInput", "type": "object"}, "CameraLicensePlateRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable LPR on this camera.", "title": "Enable LPR", "type": "boolean"}, "expire_time": {"default": 3, "description": "Time in seconds after which an unseen plate is expired from the tracker (for dedicated LPR cameras only).", "exclusiveMinimum": 0, "title": "Expire seconds", "type": "integer"}, "min_area": {"default": 1000, "description": "Minimum plate area (pixels) required to attempt recognition.", "title": "Minimum plate area", "type": "integer"}, "enhancement": {"default": 0, "description": "Enhancement level (0-10) to apply to plate crops prior to OCR; higher values may not always improve results, levels above 5 may only work with night time plates and should be used with caution.", "maximum": 10, "minimum": 0, "title": "Enhancement level", "type": "integer"}}, "title": "CameraLicensePlateRecognitionConfig", "type": "object"}, "CameraLiveConfig": {"additionalProperties": false, "properties": {"streams": {"additionalProperties": {"type": "string"}, "description": "Mapping of configured stream names to restream/go2rtc names used for live playback.", "title": "Live stream names", "type": "object"}, "height": {"default": 720, "description": "Height (pixels) to render the jsmpeg live stream in the Web UI; must be <= detect stream height.", "title": "Live height", "type": "integer"}, "quality": {"default": 8, "description": "Encoding quality for the jsmpeg stream (1 highest, 31 lowest).", "maximum": 31, "minimum": 1, "title": "Live quality", "type": "integer"}}, "title": "CameraLiveConfig", "type": "object"}, "CameraMqttConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable publishing image snapshots for objects to MQTT topics for this camera.", "title": "Send image", "type": "boolean"}, "timestamp": {"default": true, "description": "Overlay a timestamp on images published to MQTT.", "title": "Add timestamp", "type": "boolean"}, "bounding_box": {"default": true, "description": "Draw bounding boxes on images published over MQTT.", "title": "Add bounding box", "type": "boolean"}, "crop": {"default": true, "description": "Crop images published to MQTT to the detected object's bounding box.", "title": "Crop image", "type": "boolean"}, "height": {"default": 270, "description": "Height (pixels) to resize images published over MQTT.", "title": "Image height", "type": "integer"}, "required_zones": {"description": "Zones that an object must enter for an MQTT image to be published.", "items": {"type": "string"}, "title": "Required zones", "type": "array"}, "quality": {"default": 70, "description": "JPEG quality for images published to MQTT (0-100).", "maximum": 100, "minimum": 0, "title": "JPEG quality", "type": "integer"}}, "title": "CameraMqttConfig", "type": "object"}, "CameraProfileConfig": {"additionalProperties": false, "description": "A named profile containing partial camera config overrides.\n\nSections set to None inherit from the camera's base config.\nSections that are defined get Pydantic-validated, then only\nexplicitly-set fields are used as overrides via exclude_unset.", "properties": {"enabled": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Enabled"}, "audio": {"anyOf": [{"$ref": "#/$defs/AudioConfig"}, {"type": "null"}], "default": null}, "birdseye": {"anyOf": [{"$ref": "#/$defs/BirdseyeCameraConfig"}, {"type": "null"}], "default": null}, "detect": {"anyOf": [{"$ref": "#/$defs/DetectConfig"}, {"type": "null"}], "default": null}, "face_recognition": {"anyOf": [{"$ref": "#/$defs/CameraFaceRecognitionConfig"}, {"type": "null"}], "default": null}, "lpr": {"anyOf": [{"$ref": "#/$defs/CameraLicensePlateRecognitionConfig"}, {"type": "null"}], "default": null}, "motion": {"anyOf": [{"$ref": "#/$defs/MotionConfig"}, {"type": "null"}], "default": null}, "notifications": {"anyOf": [{"$ref": "#/$defs/NotificationConfig"}, {"type": "null"}], "default": null}, "objects": {"anyOf": [{"$ref": "#/$defs/ObjectConfig"}, {"type": "null"}], "default": null}, "record": {"anyOf": [{"$ref": "#/$defs/RecordConfig"}, {"type": "null"}], "default": null}, "review": {"anyOf": [{"$ref": "#/$defs/ReviewConfig"}, {"type": "null"}], "default": null}, "snapshots": {"anyOf": [{"$ref": "#/$defs/SnapshotsConfig"}, {"type": "null"}], "default": null}, "zones": {"anyOf": [{"additionalProperties": {"$ref": "#/$defs/ZoneConfig"}, "type": "object"}, {"type": "null"}], "default": null, "title": "Zones"}}, "title": "CameraProfileConfig", "type": "object"}, "CameraRoleEnum": {"enum": ["audio", "record", "record_sub", "detect"], "title": "CameraRoleEnum", "type": "string"}, "CameraSemanticSearchConfig": {"additionalProperties": false, "properties": {"triggers": {"additionalProperties": {"$ref": "#/$defs/TriggerConfig"}, "default": {}, "description": "Actions and matching criteria for camera-specific semantic search triggers.", "title": "Triggers", "type": "object"}}, "title": "CameraSemanticSearchConfig", "type": "object"}, "CameraTypeEnum": {"enum": ["generic", "lpr"], "title": "CameraTypeEnum", "type": "string"}, "CameraUiConfig": {"additionalProperties": false, "properties": {"order": {"default": 0, "description": "Numeric order used to sort the camera in the UI (default dashboard and lists); larger numbers appear later.", "title": "UI order", "type": "integer"}, "dashboard": {"default": true, "description": "Toggle whether this camera is visible on the default All Cameras live dashboard. The camera remains available everywhere else in the UI, including camera groups and settings.", "title": "Show on Live dashboard", "type": "boolean"}, "review": {"default": true, "description": "Toggle whether this camera is visible in review (the review page and its camera filter, motion review, and the history view).", "title": "Show in review", "type": "boolean"}}, "title": "CameraUiConfig", "type": "object"}, "ChaptersEnum": {"enum": ["none", "recording_segments", "review_items"], "title": "ChaptersEnum", "type": "string"}, "ClassificationConfig": {"additionalProperties": false, "properties": {"bird": {"$ref": "#/$defs/BirdClassificationConfig", "description": "Settings specific to bird classification models.", "title": "Bird classification config"}, "custom": {"additionalProperties": {"$ref": "#/$defs/CustomClassificationConfig"}, "default": {}, "description": "Configuration for custom classification models used for objects or state detection.", "title": "Custom Classification Models", "type": "object"}}, "title": "ClassificationConfig", "type": "object"}, "ColorConfig": {"additionalProperties": false, "properties": {"red": {"default": 255, "description": "Red component (0-255) for timestamp color.", "maximum": 255, "minimum": 0, "title": "Red", "type": "integer"}, "green": {"default": 255, "description": "Green component (0-255) for timestamp color.", "maximum": 255, "minimum": 0, "title": "Green", "type": "integer"}, "blue": {"default": 255, "description": "Blue component (0-255) for timestamp color.", "maximum": 255, "minimum": 0, "title": "Blue", "type": "integer"}}, "title": "ColorConfig", "type": "object"}, "CustomClassificationConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable the custom classification model.", "title": "Enable model", "type": "boolean"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Identifier for the custom classification model to use.", "title": "Model name"}, "threshold": {"default": 0.8, "description": "Score threshold used to change the classification state.", "title": "Score threshold", "type": "number"}, "save_attempts": {"anyOf": [{"minimum": 0, "type": "integer"}, {"type": "null"}], "default": null, "description": "How many classification attempts to save for recent classifications UI.", "title": "Save attempts"}, "object_config": {"anyOf": [{"$ref": "#/$defs/CustomClassificationObjectConfig"}, {"type": "null"}], "default": null}, "state_config": {"anyOf": [{"$ref": "#/$defs/CustomClassificationStateConfig"}, {"type": "null"}], "default": null}}, "title": "CustomClassificationConfig", "type": "object"}, "CustomClassificationObjectConfig": {"additionalProperties": false, "properties": {"objects": {"description": "List of object types to run object classification on.", "items": {"type": "string"}, "title": "Classify objects", "type": "array"}, "classification_type": {"$ref": "#/$defs/ObjectClassificationType", "default": "sub_label", "description": "Classification type applied: 'sub_label' (adds sub_label) or other supported types.", "title": "Classification type"}}, "title": "CustomClassificationObjectConfig", "type": "object"}, "CustomClassificationStateCameraConfig": {"additionalProperties": false, "properties": {"crop": {"description": "Crop coordinates to use for running classification on this camera.", "items": {"type": "number"}, "title": "Classification crop", "type": "array"}}, "required": ["crop"], "title": "CustomClassificationStateCameraConfig", "type": "object"}, "CustomClassificationStateConfig": {"additionalProperties": false, "properties": {"cameras": {"additionalProperties": {"$ref": "#/$defs/CustomClassificationStateCameraConfig"}, "description": "Per-camera crop and settings for running state classification.", "title": "Classification cameras", "type": "object"}, "motion": {"default": false, "description": "If true, run classification when motion is detected within the specified crop.", "title": "Run on motion", "type": "boolean"}, "interval": {"anyOf": [{"exclusiveMinimum": 0, "type": "integer"}, {"type": "null"}], "default": null, "description": "Interval (seconds) between periodic classification runs for state classification.", "title": "Classification interval"}}, "required": ["cameras"], "title": "CustomClassificationStateConfig", "type": "object"}, "DatabaseConfig": {"additionalProperties": false, "properties": {"path": {"default": "/config/frigate.db", "description": "Filesystem path where the Frigate SQLite database file will be stored.", "title": "Database path", "type": "string"}}, "title": "DatabaseConfig", "type": "object"}, "DetectConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable object detection for all cameras; can be overridden per-camera.", "title": "Enable object detection", "type": "boolean"}, "height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Height (pixels) of frames used for the detect stream; leave empty to use the native stream resolution.", "title": "Detect height"}, "width": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Width (pixels) of frames used for the detect stream; leave empty to use the native stream resolution.", "title": "Detect width"}, "fps": {"default": 5, "description": "Desired frames per second to run detection on; lower values reduce CPU usage (recommended value is 5, only set higher - at most 10 - if tracking extremely fast moving objects).", "title": "Detect FPS", "type": "integer"}, "min_initialized": {"anyOf": [{"minimum": 2, "type": "integer"}, {"type": "null"}], "default": null, "description": "Number of consecutive detection hits required before creating a tracked object. Increase to reduce false initializations. Default value is fps divided by 2.", "title": "Minimum initialization frames"}, "max_disappeared": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Number of frames without a detection before a tracked object is considered gone.", "title": "Maximum disappeared frames"}, "stationary": {"$ref": "#/$defs/StationaryConfig", "description": "Settings to detect and manage objects that remain stationary for a period of time.", "title": "Stationary objects config"}, "annotation_offset": {"default": 0, "description": "Milliseconds to shift detect annotations to better align timeline bounding boxes with recordings; can be positive or negative.", "title": "Annotation offset", "type": "integer"}}, "title": "DetectConfig", "type": "object"}, "DetectionsConfig": {"additionalProperties": false, "description": "Configure detections", "properties": {"enabled": {"default": true, "description": "Enable or disable detection events for all cameras; can be overridden per-camera.", "title": "Enable detections", "type": "boolean"}, "labels": {"anyOf": [{"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "default": null, "description": "List of object labels that qualify as detection events.", "title": "Detection labels"}, "required_zones": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Zones that an object must enter to be considered a detection; leave empty to allow any zone.", "title": "Required zones"}, "cutoff_time": {"default": 30, "description": "Seconds to wait after no detection-causing activity before cutting off a detection.", "title": "Detections cutoff time", "type": "integer"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Tracks whether detections were originally enabled in the static configuration.", "title": "Original detections state"}}, "title": "DetectionsConfig", "type": "object"}, "EnrichmentsDeviceEnum": {"enum": ["GPU", "CPU"], "title": "EnrichmentsDeviceEnum", "type": "string"}, "EventsConfig": {"additionalProperties": false, "properties": {"pre_capture": {"default": 5, "description": "Number of seconds before the detection event to include in the recording.", "maximum": 60, "minimum": 0, "title": "Pre-capture seconds", "type": "integer"}, "post_capture": {"default": 5, "description": "Number of seconds after the detection event to include in the recording.", "minimum": 0, "title": "Post-capture seconds", "type": "integer"}, "retain": {"$ref": "#/$defs/ReviewRetainConfig", "description": "Retention settings for recordings of detection events.", "title": "Event retention"}}, "title": "EventsConfig", "type": "object"}, "FaceRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable face recognition for all cameras; can be overridden per-camera.", "title": "Enable face recognition", "type": "boolean"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Model size to use for face embeddings (small/large); larger may require GPU.", "title": "Model size"}, "unknown_score": {"default": 0.8, "description": "Distance threshold below which a face is considered a potential match (higher = stricter).", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Unknown score threshold", "type": "number"}, "detection_threshold": {"default": 0.7, "description": "Minimum detection confidence required to consider a face detection valid.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Detection threshold", "type": "number"}, "recognition_threshold": {"default": 0.9, "description": "Face embedding distance threshold to consider two faces a match.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Recognition threshold", "type": "number"}, "min_area": {"default": 750, "description": "Minimum area (pixels) of a detected face box required to attempt recognition.", "title": "Minimum face area", "type": "integer"}, "min_faces": {"default": 1, "description": "Minimum number of face recognitions required before applying a recognized sub-label to a person.", "exclusiveMinimum": 0, "maximum": 6, "title": "Minimum faces", "type": "integer"}, "save_attempts": {"default": 200, "description": "Number of face recognition attempts to retain for recent recognition UI.", "minimum": 0, "title": "Save attempts", "type": "integer"}, "blur_confidence_filter": {"default": true, "description": "Adjust confidence scores based on image blur to reduce false positives for poor quality faces.", "title": "Blur confidence filter", "type": "boolean"}, "device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information", "title": "Device"}}, "title": "FaceRecognitionConfig", "type": "object"}, "FfmpegConfig": {"additionalProperties": false, "properties": {"path": {"default": "default", "description": "Path to the FFmpeg binary to use or a version alias (\"7.0\" or \"8.0\").", "title": "FFmpeg path", "type": "string"}, "global_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "description": "Global arguments passed to FFmpeg processes.", "title": "FFmpeg global arguments"}, "hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "auto", "description": "Hardware acceleration arguments for FFmpeg. Provider-specific presets are recommended.", "title": "Hardware acceleration arguments"}, "input_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "preset-rtsp-generic", "description": "Input arguments applied to FFmpeg input streams.", "title": "Input arguments"}, "output_args": {"$ref": "#/$defs/FfmpegOutputArgsConfig", "description": "Default output arguments used for different FFmpeg roles such as detect and record.", "title": "Output arguments"}, "retry_interval": {"default": 10.0, "description": "Seconds to wait before attempting to reconnect a camera stream after failure. Default is 10.", "exclusiveMinimum": 0.0, "title": "FFmpeg retry time", "type": "number"}, "apple_compatibility": {"default": false, "description": "Enable HEVC tagging for better Apple player compatibility when recording H.265.", "title": "Apple compatibility", "type": "boolean"}, "gpu": {"default": 0, "description": "Default GPU index used for hardware acceleration if available.", "title": "GPU index", "type": "integer"}}, "title": "FfmpegConfig", "type": "object"}, "FfmpegOutputArgsConfig": {"additionalProperties": false, "properties": {"detect": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "description": "Default output arguments for detect role streams.", "title": "Detect output arguments"}, "record": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "preset-record-generic-audio-aac", "description": "Default output arguments for record role streams.", "title": "Record output arguments"}, "record_sub": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Output arguments for record_sub role streams. The record output arguments are used when this is not set.", "title": "Sub stream record output arguments"}}, "title": "FfmpegOutputArgsConfig", "type": "object"}, "FilterConfig": {"additionalProperties": false, "properties": {"min_area": {"anyOf": [{"type": "integer"}, {"type": "number"}], "default": 0, "description": "Minimum bounding box area (pixels or percentage) required for this object type. Can be pixels (int) or percentage (float between 0.000001 and 0.99).", "title": "Minimum object area"}, "max_area": {"anyOf": [{"type": "integer"}, {"type": "number"}], "default": 24000000, "description": "Maximum bounding box area (pixels or percentage) allowed for this object type. Can be pixels (int) or percentage (float between 0.000001 and 0.99).", "title": "Maximum object area"}, "min_ratio": {"default": 0, "description": "Minimum width/height ratio required for the bounding box to qualify.", "title": "Minimum aspect ratio", "type": "number"}, "max_ratio": {"default": 24000000, "description": "Maximum width/height ratio allowed for the bounding box to qualify.", "title": "Maximum aspect ratio", "type": "number"}, "threshold": {"default": 0.7, "description": "Average detection confidence threshold required for the object to be considered a true positive.", "title": "Confidence threshold", "type": "number"}, "min_score": {"default": 0.5, "description": "Minimum single-frame detection confidence required for the object to be counted.", "title": "Minimum confidence", "type": "number"}, "mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "description": "Polygon coordinates defining where this filter applies within the frame.", "title": "Filter mask", "type": "object"}, "raw_mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "title": "Raw Mask", "type": "object"}}, "title": "FilterConfig", "type": "object"}, "GenAIConfig": {"additionalProperties": false, "description": "Primary GenAI Config to define GenAI Provider.", "properties": {"api_key": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "API key required by some providers (can also be set via environment variables).", "title": "API key"}, "base_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Base URL for self-hosted or compatible providers (for example an Ollama instance).", "title": "Base URL"}, "model": {"default": "", "description": "The model to use from the provider for generating descriptions or summaries.", "title": "Model", "type": "string"}, "provider": {"$ref": "#/$defs/GenAIProviderEnum", "description": "The GenAI provider to use (for example: ollama, gemini, openai).", "title": "Provider"}, "roles": {"description": "GenAI roles (chat, descriptions, embeddings); one provider per role.", "items": {"$ref": "#/$defs/GenAIRoleEnum"}, "title": "Roles", "type": "array"}, "provider_options": {"additionalProperties": {}, "default": {}, "description": "Additional provider-specific options to pass to the GenAI client.", "title": "Provider options", "type": "object"}, "runtime_options": {"additionalProperties": {}, "default": {}, "description": "Runtime options passed to the provider for each inference call.", "title": "Runtime options", "type": "object"}}, "required": ["provider"], "title": "GenAIConfig", "type": "object"}, "GenAIObjectConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable GenAI generation of descriptions for tracked objects by default.", "title": "Enable GenAI", "type": "boolean"}, "use_snapshot": {"default": false, "description": "Use object snapshots instead of thumbnails for GenAI description generation.", "title": "Use snapshots", "type": "boolean"}, "prompt": {"default": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "description": "Default prompt template used when generating descriptions with GenAI.", "title": "Caption prompt", "type": "string"}, "object_prompts": {"additionalProperties": {"type": "string"}, "description": "Per-object prompts to customize GenAI outputs for specific labels.", "title": "Object prompts", "type": "object"}, "objects": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "List of object labels to send to GenAI by default.", "title": "GenAI objects"}, "required_zones": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Zones that must be entered for objects to qualify for GenAI description generation.", "title": "Required zones"}, "debug_save_thumbnails": {"default": false, "description": "Save thumbnails sent to GenAI for debugging and review.", "title": "Save thumbnails", "type": "boolean"}, "send_triggers": {"$ref": "#/$defs/GenAIObjectTriggerConfig", "description": "Defines when frames should be sent to GenAI (on end, after updates, etc.).", "title": "GenAI triggers"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether GenAI was enabled in the original static config.", "title": "Original GenAI state"}}, "title": "GenAIObjectConfig", "type": "object"}, "GenAIObjectTriggerConfig": {"additionalProperties": false, "properties": {"tracked_object_end": {"default": true, "description": "Send a request to GenAI when the tracked object ends.", "title": "Send on end", "type": "boolean"}, "after_significant_updates": {"anyOf": [{"minimum": 1, "type": "integer"}, {"type": "null"}], "default": null, "description": "Send a request to GenAI after a specified number of significant updates for the tracked object.", "title": "Early GenAI trigger"}}, "title": "GenAIObjectTriggerConfig", "type": "object"}, "GenAIProviderEnum": {"enum": ["openai", "azure_openai", "gemini", "ollama", "llamacpp"], "title": "GenAIProviderEnum", "type": "string"}, "GenAIReviewConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable GenAI-generated descriptions and summaries for review items.", "title": "Enable GenAI descriptions", "type": "boolean"}, "alerts": {"default": true, "description": "Use GenAI to generate descriptions for alert items.", "title": "Enable GenAI for alerts", "type": "boolean"}, "detections": {"default": false, "description": "Use GenAI to generate descriptions for detection items.", "title": "Enable GenAI for detections", "type": "boolean"}, "image_source": {"$ref": "#/$defs/ImageSourceEnum", "default": "preview", "description": "Source of images sent to GenAI ('preview' or 'recordings'); 'recordings' uses higher quality frames but more tokens.", "title": "Review image source"}, "additional_concerns": {"default": [], "description": "A list of additional concerns or notes the GenAI should consider when evaluating activity on this camera.", "items": {"type": "string"}, "title": "Additional concerns", "type": "array"}, "debug_save_thumbnails": {"default": false, "description": "Save thumbnails that are sent to the GenAI provider for debugging and review.", "title": "Save thumbnails", "type": "boolean"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Tracks whether GenAI review was originally enabled in the static configuration.", "title": "Original GenAI state"}, "preferred_language": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Preferred language to request from the GenAI provider for generated responses.", "title": "Preferred language"}, "activity_context_prompt": {"default": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is.", "description": "Custom prompt describing what is and is not suspicious activity to provide context for GenAI summaries.", "title": "Activity context prompt", "type": "string"}}, "title": "GenAIReviewConfig", "type": "object"}, "GenAIRoleEnum": {"enum": ["chat", "descriptions", "embeddings"], "title": "GenAIRoleEnum", "type": "string"}, "HeaderMappingConfig": {"additionalProperties": false, "properties": {"user": {"default": null, "description": "Header containing the authenticated username provided by the upstream proxy.", "title": "User header", "type": "string"}, "role": {"default": null, "description": "Header containing the authenticated user's role or groups from the upstream proxy.", "title": "Role header", "type": "string"}, "role_map": {"anyOf": [{"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "type": "object"}, {"type": "null"}], "description": "Map upstream group values to Frigate roles (for example map admin groups to the admin role).", "title": "Role mapping"}}, "title": "HeaderMappingConfig", "type": "object"}, "IPv6Config": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable IPv6 support for Frigate services (API and UI) where applicable.", "title": "Enable IPv6", "type": "boolean"}}, "title": "IPv6Config", "type": "object"}, "ImageSourceEnum": {"description": "Image source options for GenAI Review.", "enum": ["preview", "recordings"], "title": "ImageSourceEnum", "type": "string"}, "InputDTypeEnum": {"enum": ["float", "float_denorm", "int"], "title": "InputDTypeEnum", "type": "string"}, "InputTensorEnum": {"enum": ["nchw", "nhwc", "hwnc", "hwcn"], "title": "InputTensorEnum", "type": "string"}, "LicensePlateRecognitionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable license plate recognition for all cameras; can be overridden per-camera.", "title": "Enable LPR", "type": "boolean"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Model size used for text detection/recognition. Most users should use 'small'.", "title": "Model size"}, "detection_threshold": {"default": 0.7, "description": "Detection confidence threshold to begin running OCR on a suspected plate.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Detection threshold", "type": "number"}, "min_area": {"default": 1000, "description": "Minimum plate area (pixels) required to attempt recognition.", "title": "Minimum plate area", "type": "integer"}, "recognition_threshold": {"default": 0.9, "description": "Confidence threshold required for recognized plate text to be attached as a sub-label.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Recognition threshold", "type": "number"}, "min_plate_length": {"default": 4, "description": "Minimum number of characters a recognized plate must contain to be considered valid.", "title": "Min plate length", "type": "integer"}, "format": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional regex to validate recognized plate strings against an expected format.", "title": "Plate format regex"}, "match_distance": {"default": 1, "description": "Number of character mismatches allowed when comparing detected plates to known plates.", "minimum": 0, "title": "Match distance", "type": "integer"}, "known_plates": {"anyOf": [{"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "type": "object"}, {"type": "null"}], "default": {}, "description": "List of plates or regexes to specially track or alert on.", "title": "Known plates"}, "enhancement": {"default": 0, "description": "Enhancement level (0-10) to apply to plate crops prior to OCR; higher values may not always improve results, levels above 5 may only work with night time plates and should be used with caution.", "maximum": 10, "minimum": 0, "title": "Enhancement level", "type": "integer"}, "debug_save_plates": {"default": false, "description": "Save plate crop images for debugging LPR performance.", "title": "Save debug plates", "type": "boolean"}, "device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information", "title": "Device"}, "replace_rules": {"description": "Regex replacement rules used to normalize detected plate strings before matching.", "items": {"$ref": "#/$defs/ReplaceRule"}, "title": "Replacement rules", "type": "array"}}, "title": "LicensePlateRecognitionConfig", "type": "object"}, "ListenConfig": {"additionalProperties": false, "properties": {"internal": {"anyOf": [{"type": "integer"}, {"type": "string"}], "default": 5000, "description": "Internal listening port for Frigate (default 5000).", "title": "Internal port"}, "external": {"anyOf": [{"type": "integer"}, {"type": "string"}], "default": 8971, "description": "External listening port for Frigate (default 8971).", "title": "External port"}}, "title": "ListenConfig", "type": "object"}, "LogLevel": {"enum": ["debug", "info", "warning", "error", "critical"], "title": "LogLevel", "type": "string"}, "LoggerConfig": {"additionalProperties": false, "properties": {"default": {"$ref": "#/$defs/LogLevel", "default": "info", "title": "Logging level", "description": "Default global log verbosity (debug, info, warning, error)."}, "logs": {"additionalProperties": {"$ref": "#/$defs/LogLevel"}, "description": "Per-component log level overrides to increase or decrease verbosity for specific modules.", "title": "Per-process log level", "type": "object"}}, "title": "LoggerConfig", "type": "object"}, "ModelConfig": {"additionalProperties": false, "properties": {"path": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Path to a custom detection model file (or plus:// for Frigate+ models).", "title": "Custom object detector model path"}, "labelmap_path": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Path to a labelmap file that maps numeric classes to string labels for the detector.", "title": "Label map for custom object detector"}, "width": {"default": 320, "description": "Width of the model input tensor in pixels.", "title": "Object detection model input width", "type": "integer"}, "height": {"default": 320, "description": "Height of the model input tensor in pixels.", "title": "Object detection model input height", "type": "integer"}, "labelmap": {"additionalProperties": {"type": "string"}, "description": "Overrides or remapping entries to merge into the standard labelmap.", "title": "Labelmap customization", "type": "object"}, "attributes_map": {"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "default": {"person": ["amazon", "face"], "car": ["amazon", "an_post", "canada_post", "dhl", "dpd", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "motorcycle": ["license_plate"]}, "description": "Mapping from object labels to attribute labels used to attach metadata (for example 'car' -> ['license_plate']).", "title": "Map of object labels to their attribute labels", "type": "object"}, "input_tensor": {"$ref": "#/$defs/InputTensorEnum", "default": "nhwc", "description": "Tensor format expected by the model: 'nhwc' or 'nchw'.", "title": "Model Input Tensor Shape"}, "input_pixel_format": {"$ref": "#/$defs/PixelFormatEnum", "default": "rgb", "description": "Pixel colorspace expected by the model: 'rgb', 'bgr', or 'yuv'.", "title": "Model Input Pixel Color Format"}, "input_dtype": {"$ref": "#/$defs/InputDTypeEnum", "default": "int", "description": "Data type of the model input tensor (for example 'float32').", "title": "Model Input D Type"}, "model_type": {"$ref": "#/$defs/ModelTypeEnum", "default": "ssd", "description": "Detector model architecture type (ssd, yolox, yolonas, yolo-generic, rfdetr, dfine) used by some detectors for optimization.", "title": "Object Detection Model Type"}}, "title": "ModelConfig", "type": "object"}, "ModelSizeEnum": {"enum": ["small", "large"], "title": "ModelSizeEnum", "type": "string"}, "ModelTypeEnum": {"enum": ["dfine", "rfdetr", "ssd", "yolox", "yolonas", "yolo-generic"], "title": "ModelTypeEnum", "type": "string"}, "MotionConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable motion detection for all cameras; can be overridden per-camera.", "title": "Enable motion detection", "type": "boolean"}, "threshold": {"default": 30, "description": "Pixel difference threshold used by the motion detector; higher values reduce sensitivity (range 1-255).", "maximum": 255, "minimum": 1, "title": "Motion threshold", "type": "integer"}, "lightning_threshold": {"default": 0.8, "description": "Threshold to detect and ignore brief lighting spikes (lower is more sensitive, values between 0.3 and 1.0). This does not prevent motion detection entirely; it merely causes the detector to stop analyzing additional frames once the threshold is exceeded. Motion-based recordings are still created during these events.", "maximum": 1.0, "minimum": 0.3, "title": "Lightning threshold", "type": "number"}, "skip_motion_threshold": {"anyOf": [{"maximum": 1.0, "minimum": 0.0, "type": "number"}, {"type": "null"}], "default": null, "description": "If set to a value between 0.0 and 1.0, and more than this fraction of the image changes in a single frame, the detector will return no motion boxes and immediately recalibrate. This can save CPU and reduce false positives during lightning, storms, etc., but may miss real events such as a PTZ camera auto\u2011tracking an object. The trade\u2011off is between dropping a few megabytes of recordings versus reviewing a couple short clips. Leave unset (None) to disable this feature.", "title": "Skip motion threshold"}, "improve_contrast": {"default": true, "description": "Apply contrast improvement to frames before motion analysis to help detection.", "title": "Improve contrast", "type": "boolean"}, "contour_area": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": 10, "description": "Minimum contour area in pixels required for a motion contour to be counted.", "title": "Contour area"}, "delta_alpha": {"default": 0.2, "description": "Alpha blending factor used in frame differencing for motion calculation.", "title": "Delta alpha", "type": "number"}, "frame_alpha": {"default": 0.01, "description": "Alpha value used when blending frames for motion preprocessing.", "title": "Frame alpha", "type": "number"}, "frame_height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": 100, "description": "Height in pixels to scale frames to when computing motion.", "title": "Frame height"}, "mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/MotionMaskConfig"}, {"type": "null"}]}, "description": "Ordered x,y coordinates defining the motion mask polygon used to include/exclude areas.", "title": "Mask coordinates", "type": "object"}, "mqtt_off_delay": {"default": 30, "description": "Seconds to wait after last motion before publishing an MQTT 'off' state.", "title": "MQTT off delay", "type": "integer"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether motion detection was enabled in the original static configuration.", "title": "Original motion state"}, "raw_mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/MotionMaskConfig"}, {"type": "null"}]}, "title": "Raw Mask", "type": "object"}}, "title": "MotionConfig", "type": "object"}, "MotionMaskConfig": {"additionalProperties": false, "description": "Configuration for a single motion mask.", "properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "A friendly name for this motion mask used in the Frigate UI", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enable or disable this motion mask", "title": "Enabled", "type": "boolean"}, "coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "description": "Ordered x,y coordinates defining the motion mask polygon used to include/exclude areas.", "title": "Coordinates"}, "raw_coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "title": "Raw Coordinates"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Keep track of original state of motion mask."}}, "title": "MotionMaskConfig", "type": "object"}, "MqttConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable or disable MQTT integration for state, events, and snapshots.", "title": "Enable MQTT", "type": "boolean"}, "host": {"default": "", "description": "Hostname or IP address of the MQTT broker.", "title": "MQTT host", "type": "string"}, "port": {"default": 1883, "description": "Port of the MQTT broker (usually 1883 for plain MQTT).", "title": "MQTT port", "type": "integer"}, "topic_prefix": {"default": "frigate", "description": "MQTT topic prefix for all Frigate topics; must be unique if running multiple instances.", "title": "Topic prefix", "type": "string"}, "client_id": {"default": "frigate", "description": "Client identifier used when connecting to the MQTT broker; should be unique per instance.", "title": "Client ID", "type": "string"}, "stats_interval": {"default": 60, "description": "Interval in seconds for publishing system and camera stats to MQTT.", "minimum": 15, "title": "Stats interval", "type": "integer"}, "user": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional MQTT username; can be provided via environment variables or secrets.", "title": "MQTT username"}, "password": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional MQTT password; can be provided via environment variables or secrets.", "title": "MQTT password"}, "tls_ca_certs": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Path to CA certificate for TLS connections to the broker (for self-signed certs).", "title": "TLS CA certs"}, "tls_client_cert": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Client certificate path for TLS mutual authentication; do not set user/password when using client certs.", "title": "Client cert"}, "tls_client_key": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Private key path for the client certificate.", "title": "Client key"}, "tls_insecure": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Allow insecure TLS connections by skipping hostname verification (not recommended).", "title": "TLS insecure"}, "qos": {"default": 0, "description": "Quality of Service level for MQTT publishes/subscriptions (0, 1, or 2).", "title": "MQTT QoS", "type": "integer"}}, "title": "MqttConfig", "type": "object"}, "NetworkingConfig": {"additionalProperties": false, "properties": {"ipv6": {"$ref": "#/$defs/IPv6Config", "description": "IPv6-specific settings for Frigate network services.", "title": "IPv6 configuration"}, "listen": {"$ref": "#/$defs/ListenConfig", "description": "Configuration for internal and external listening ports. This is for advanced users. For the majority of use cases it's recommended to change the ports section of your Docker compose file.", "title": "Listening ports configuration"}}, "title": "NetworkingConfig", "type": "object"}, "NotificationConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable notifications for all cameras; can be overridden per-camera.", "title": "Enable notifications", "type": "boolean"}, "email": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Email address used for push notifications or required by certain notification providers.", "title": "Notification email"}, "cooldown": {"default": 0, "description": "Cooldown (seconds) between notifications to avoid spamming recipients.", "minimum": 0, "title": "Cooldown period", "type": "integer"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether notifications were enabled in the original static configuration.", "title": "Original notifications state"}}, "title": "NotificationConfig", "type": "object"}, "ObjectClassificationType": {"enum": ["sub_label", "attribute"], "title": "ObjectClassificationType", "type": "string"}, "ObjectConfig": {"additionalProperties": false, "properties": {"track": {"default": ["person"], "description": "List of object labels to track for all cameras; can be overridden per-camera.", "items": {"type": "string"}, "title": "Objects to track", "type": "array"}, "filters": {"additionalProperties": {"$ref": "#/$defs/FilterConfig"}, "description": "Filters applied to detected objects to reduce false positives (area, ratio, confidence).", "title": "Object filters", "type": "object"}, "mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "description": "Mask polygon used to prevent object detection in specified areas.", "title": "Object mask", "type": "object"}, "raw_mask": {"additionalProperties": {"anyOf": [{"$ref": "#/$defs/ObjectMaskConfig"}, {"type": "null"}]}, "title": "Raw Mask", "type": "object"}, "genai": {"$ref": "#/$defs/GenAIObjectConfig", "description": "GenAI options for describing tracked objects and sending frames for generation.", "title": "GenAI object config"}}, "title": "ObjectConfig", "type": "object"}, "ObjectMaskConfig": {"additionalProperties": false, "description": "Configuration for a single object mask.", "properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "A friendly name for this object mask used in the Frigate UI", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enable or disable this object mask", "title": "Enabled", "type": "boolean"}, "coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "description": "Ordered x,y coordinates defining the object mask polygon used to include/exclude areas.", "title": "Coordinates"}, "raw_coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "", "title": "Raw Coordinates"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Keep track of original state of object mask."}}, "title": "ObjectMaskConfig", "type": "object"}, "OnvifConfig": {"additionalProperties": false, "properties": {"host": {"default": "", "description": "Host (and optional scheme) for the ONVIF service for this camera.", "title": "ONVIF host", "type": "string"}, "port": {"default": 8000, "description": "Port number for the ONVIF service.", "title": "ONVIF port", "type": "integer"}, "user": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Username for ONVIF authentication; some devices require admin user for ONVIF.", "title": "ONVIF username"}, "password": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Password for ONVIF authentication.", "title": "ONVIF password"}, "tls_insecure": {"default": false, "description": "Skip TLS verification and disable digest auth for ONVIF (unsafe; use in safe networks only).", "title": "Disable TLS verify", "type": "boolean"}, "profile": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Specific ONVIF media profile to use for PTZ control, matched by token or name. If not set, the first profile with valid PTZ configuration is selected automatically.", "title": "ONVIF profile"}, "autotracking": {"$ref": "#/$defs/PtzAutotrackConfig", "description": "Automatically track moving objects and keep them centered in the frame using PTZ camera movements.", "title": "Autotracking"}, "ignore_time_mismatch": {"default": false, "description": "Ignore time synchronization differences between camera and Frigate server for ONVIF communication.", "title": "Ignore time mismatch", "type": "boolean"}}, "title": "OnvifConfig", "type": "object"}, "PixelFormatEnum": {"enum": ["rgb", "bgr", "yuv"], "title": "PixelFormatEnum", "type": "string"}, "ProfileDefinitionConfig": {"additionalProperties": false, "description": "Defines a named profile with a human-readable display name.\n\nThe dict key is the machine name used internally; friendly_name\nis the label shown in the UI and API responses.", "properties": {"friendly_name": {"description": "Display name for this profile shown in the UI.", "title": "Friendly name", "type": "string"}}, "required": ["friendly_name"], "title": "ProfileDefinitionConfig", "type": "object"}, "ProxyConfig": {"additionalProperties": false, "properties": {"header_map": {"$ref": "#/$defs/HeaderMappingConfig", "description": "Map incoming proxy headers to Frigate user and role fields for proxy-based auth.", "title": "Header mapping"}, "logout_url": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "URL to redirect users to when logging out via the proxy.", "title": "Logout URL"}, "auth_secret": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional secret checked against the X-Proxy-Secret header to verify trusted proxies.", "title": "Proxy secret"}, "default_role": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": "viewer", "description": "Default role assigned to proxy-authenticated users when no role mapping applies.", "title": "Default role"}, "separator": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": ",", "description": "Character used to split multiple values provided in proxy headers.", "title": "Separator character"}}, "title": "ProxyConfig", "type": "object"}, "PtzAutotrackConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable automatic PTZ camera tracking of detected objects.", "title": "Enable Autotracking", "type": "boolean"}, "calibrate_on_startup": {"default": false, "description": "Measure PTZ motor speeds on startup to improve tracking accuracy. Frigate will update config with movement_weights after calibration.", "title": "Calibrate on start", "type": "boolean"}, "zooming": {"$ref": "#/$defs/ZoomingModeEnum", "default": "disabled", "description": "Control zoom behavior: disabled (pan/tilt only), absolute (most compatible), or relative (concurrent pan/tilt/zoom).", "title": "Zoom mode"}, "zoom_factor": {"default": 0.3, "description": "Control zoom level on tracked objects. Lower values keep more scene in view; higher values zoom in closer but may lose tracking. Values between 0.1 and 0.75.", "maximum": 0.75, "minimum": 0.1, "title": "Zoom factor", "type": "number"}, "track": {"default": ["person"], "description": "List of object types that should trigger autotracking.", "items": {"type": "string"}, "title": "Tracked objects", "type": "array"}, "required_zones": {"description": "Objects must enter one of these zones before autotracking begins.", "items": {"type": "string"}, "title": "Required zones", "type": "array"}, "return_preset": {"default": "home", "description": "ONVIF preset name configured in camera firmware to return to after tracking ends.", "title": "Return preset", "type": "string"}, "timeout": {"default": 10, "description": "Wait this many seconds after losing tracking before returning camera to preset position.", "title": "Return timeout", "type": "integer"}, "movement_weights": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "description": "Calibration values automatically generated by camera calibration. Do not modify manually.", "title": "Movement weights"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Internal field to track whether autotracking was enabled in configuration.", "title": "Original autotrack state"}}, "title": "PtzAutotrackConfig", "type": "object"}, "RecordConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable recording for all cameras; can be overridden per-camera.", "title": "Enable recording", "type": "boolean"}, "expire_interval": {"default": 60, "description": "Minutes between cleanup passes that remove expired recording segments.", "title": "Record cleanup interval", "type": "integer"}, "continuous": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain recordings regardless of tracked objects or motion. Set to 0 if you only want to retain recordings of alerts and detections.", "title": "Continuous retention"}, "motion": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain recordings triggered by motion regardless of tracked objects. Set to 0 if you only want to retain recordings of alerts and detections.", "title": "Motion retention"}, "detections": {"$ref": "#/$defs/EventsConfig", "description": "Recording retention settings for detection events including pre/post capture durations.", "title": "Detection retention"}, "alerts": {"$ref": "#/$defs/EventsConfig", "description": "Recording retention settings for alert events including pre/post capture durations.", "title": "Alert retention"}, "export": {"$ref": "#/$defs/RecordExportConfig", "description": "Settings used when exporting recordings such as timelapse and hardware acceleration.", "title": "Export config"}, "preview": {"$ref": "#/$defs/RecordPreviewConfig", "description": "Settings controlling the quality of recording previews shown in the UI.", "title": "Preview config"}, "sub": {"$ref": "#/$defs/RecordSubConfig", "description": "Settings for recording a second, lower quality stream.", "title": "Sub stream recording"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "description": "Indicates whether recording was enabled in the original static configuration.", "title": "Original recording state"}}, "title": "RecordConfig", "type": "object"}, "RecordExportConfig": {"additionalProperties": false, "properties": {"hwaccel_args": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "default": "auto", "description": "Hardware acceleration args to use for export/transcode operations.", "title": "Export hwaccel args"}, "max_concurrent": {"default": 3, "description": "Maximum number of export jobs to process at the same time.", "minimum": 1, "title": "Maximum concurrent exports", "type": "integer"}, "chapters": {"$ref": "#/$defs/ChaptersEnum", "default": "review_items", "title": "Chapter metadata to embed in exported recordings"}}, "title": "RecordExportConfig", "type": "object"}, "RecordPreviewConfig": {"additionalProperties": false, "properties": {"quality": {"$ref": "#/$defs/RecordQualityEnum", "default": "medium", "description": "Preview quality level (very_low, low, medium, high, very_high).", "title": "Preview quality"}}, "title": "RecordPreviewConfig", "type": "object"}, "RecordQualityEnum": {"enum": ["very_low", "low", "medium", "high", "very_high"], "title": "RecordQualityEnum", "type": "string"}, "RecordRetainConfig": {"additionalProperties": false, "properties": {"days": {"default": 0, "description": "Days to retain recordings.", "minimum": 0.0, "title": "Retention days", "type": "number"}}, "title": "RecordRetainConfig", "type": "object"}, "RecordSubConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable recording of a second, lower quality stream for adaptive quality playback and extended retention.", "title": "Enable sub stream recording", "type": "boolean"}, "continuous": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain sub stream recordings regardless of tracked objects or motion.", "title": "Sub stream continuous retention"}, "motion": {"$ref": "#/$defs/RecordRetainConfig", "description": "Number of days to retain sub stream recordings triggered by motion.", "title": "Sub stream motion retention"}, "alerts": {"$ref": "#/$defs/ReviewRetainConfig", "description": "Retention settings for sub stream recordings of alerts.", "title": "Sub stream alert retention"}, "detections": {"$ref": "#/$defs/ReviewRetainConfig", "description": "Retention settings for sub stream recordings of detections.", "title": "Sub stream detection retention"}}, "title": "RecordSubConfig", "type": "object"}, "ReplaceRule": {"additionalProperties": false, "properties": {"pattern": {"title": "Regex pattern", "type": "string"}, "replacement": {"title": "Replacement string", "type": "string"}}, "required": ["pattern", "replacement"], "title": "ReplaceRule", "type": "object"}, "RestreamConfig": {"additionalProperties": true, "properties": {}, "title": "RestreamConfig", "type": "object"}, "RetainConfig": {"additionalProperties": false, "properties": {"default": {"type": "number", "default": 10, "title": "Default retention", "description": "Default number of days to retain snapshots."}, "objects": {"additionalProperties": {"type": "number"}, "description": "Per-object overrides for snapshot retention days.", "title": "Object retention", "type": "object"}}, "title": "RetainConfig", "type": "object"}, "RetainModeEnum": {"enum": ["all", "motion", "active_objects"], "title": "RetainModeEnum", "type": "string"}, "ReviewConfig": {"additionalProperties": false, "properties": {"alerts": {"$ref": "#/$defs/AlertsConfig", "description": "Settings for which tracked objects generate alerts and how alerts are retained.", "title": "Alerts config"}, "detections": {"$ref": "#/$defs/DetectionsConfig", "description": "Settings for which tracked objects generate detections (non-alert) and how detections are retained.", "title": "Detections config"}, "genai": {"$ref": "#/$defs/GenAIReviewConfig", "description": "Controls use of generative AI for producing descriptions and summaries of review items.", "title": "GenAI config"}}, "title": "ReviewConfig", "type": "object"}, "ReviewRetainConfig": {"additionalProperties": false, "properties": {"days": {"default": 10, "description": "Number of days to retain recordings of detection events.", "minimum": 0.0, "title": "Retention days", "type": "number"}, "mode": {"$ref": "#/$defs/RetainModeEnum", "default": "motion", "description": "Mode for retention: all (save all segments), motion (save segments with motion), or active_objects (save segments with active objects).", "title": "Retention mode"}}, "title": "ReviewRetainConfig", "type": "object"}, "SemanticSearchConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable the semantic search feature.", "title": "Enable semantic search", "type": "boolean"}, "reindex": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": false, "description": "Trigger a full reindex of historical tracked objects into the embeddings database.", "title": "Reindex on startup"}, "model": {"anyOf": [{"$ref": "#/$defs/SemanticSearchModelEnum"}, {"type": "string"}, {"type": "null"}], "default": "jinav1", "description": "The embeddings model to use for semantic search (for example 'jinav1'), or the name of a GenAI provider with the embeddings role.", "title": "Semantic search model or GenAI provider name"}, "model_size": {"$ref": "#/$defs/ModelSizeEnum", "default": "small", "description": "Select model size; 'small' runs on CPU and 'large' typically requires GPU.", "title": "Model size"}, "device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information", "title": "Device"}}, "title": "SemanticSearchConfig", "type": "object"}, "SemanticSearchModelEnum": {"enum": ["jinav1", "jinav2"], "title": "SemanticSearchModelEnum", "type": "string"}, "SnapshotsConfig": {"additionalProperties": false, "properties": {"enabled": {"default": false, "description": "Enable or disable saving snapshots for all cameras; can be overridden per-camera.", "title": "Enable snapshots", "type": "boolean"}, "timestamp": {"default": false, "description": "Overlay a timestamp on snapshots from API.", "title": "Timestamp overlay", "type": "boolean"}, "bounding_box": {"default": true, "description": "Draw bounding boxes for tracked objects on snapshots from API.", "title": "Bounding box overlay", "type": "boolean"}, "crop": {"default": false, "description": "Crop snapshots from API to the detected object's bounding box.", "title": "Crop snapshot", "type": "boolean"}, "required_zones": {"description": "Zones an object must enter for a snapshot to be saved.", "items": {"type": "string"}, "title": "Required zones", "type": "array"}, "height": {"anyOf": [{"type": "integer"}, {"type": "null"}], "default": null, "description": "Height (pixels) to resize snapshots from API to; leave empty to preserve original size.", "title": "Snapshot height"}, "retain": {"$ref": "#/$defs/RetainConfig", "description": "Retention settings for snapshots including default days and per-object overrides.", "title": "Snapshot retention"}, "quality": {"default": 60, "description": "Encode quality for saved snapshots (0-100).", "maximum": 100, "minimum": 0, "title": "Snapshot quality", "type": "integer"}}, "title": "SnapshotsConfig", "type": "object"}, "StationaryConfig": {"additionalProperties": false, "properties": {"interval": {"anyOf": [{"exclusiveMinimum": 0, "type": "integer"}, {"type": "null"}], "default": null, "description": "How often (in frames) to run a detection check to confirm a stationary object.", "title": "Stationary interval"}, "threshold": {"anyOf": [{"minimum": 1, "type": "integer"}, {"type": "null"}], "default": null, "description": "Number of frames with no position change required to mark an object as stationary.", "title": "Stationary threshold"}, "max_frames": {"$ref": "#/$defs/StationaryMaxFramesConfig", "description": "Limits how long stationary objects are tracked before being discarded.", "title": "Max frames"}, "classifier": {"default": true, "description": "Use a visual classifier to detect truly stationary objects even when bounding boxes jitter.", "title": "Enable visual classifier", "type": "boolean"}}, "title": "StationaryConfig", "type": "object"}, "StationaryMaxFramesConfig": {"additionalProperties": false, "properties": {"default": {"anyOf": [{"minimum": 1, "type": "integer"}, {"type": "null"}], "default": null, "title": "Default max frames", "description": "Default maximum frames to track a stationary object before stopping."}, "objects": {"additionalProperties": {"type": "integer"}, "description": "Per-object overrides for maximum frames to track stationary objects.", "title": "Object max frames", "type": "object"}}, "title": "StationaryMaxFramesConfig", "type": "object"}, "StatsConfig": {"additionalProperties": false, "properties": {"amd_gpu_stats": {"default": true, "description": "Enable collection of AMD GPU statistics if an AMD GPU is present.", "title": "AMD GPU stats", "type": "boolean"}, "intel_gpu_stats": {"default": true, "description": "Enable collection of Intel GPU statistics if an Intel GPU is present.", "title": "Intel GPU stats", "type": "boolean"}, "network_bandwidth": {"default": false, "description": "Enable per-process network bandwidth monitoring for camera ffmpeg processes and detectors (requires capabilities).", "title": "Network bandwidth", "type": "boolean"}, "intel_gpu_device": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "PCI bus address or DRM device path (e.g. /dev/dri/card1) used to pin Intel GPU stats to a specific device when multiple are present.", "title": "Intel GPU device"}}, "title": "StatsConfig", "type": "object"}, "TelemetryConfig": {"additionalProperties": false, "properties": {"network_interfaces": {"default": [], "description": "List of network interface name prefixes to monitor for bandwidth statistics.", "items": {"type": "string"}, "title": "Network interfaces", "type": "array"}, "stats": {"$ref": "#/$defs/StatsConfig", "description": "Options to enable/disable collection of various system and GPU statistics.", "title": "System stats"}, "version_check": {"default": true, "description": "Enable an outbound check to detect if a newer Frigate version is available.", "title": "Version check", "type": "boolean"}}, "title": "TelemetryConfig", "type": "object"}, "TimeFormatEnum": {"enum": ["browser", "12hour", "24hour"], "title": "TimeFormatEnum", "type": "string"}, "TimestampEffectEnum": {"enum": ["solid", "shadow"], "title": "TimestampEffectEnum", "type": "string"}, "TimestampPositionEnum": {"enum": ["tl", "tr", "bl", "br"], "title": "TimestampPositionEnum", "type": "string"}, "TimestampStyleConfig": {"additionalProperties": false, "properties": {"position": {"$ref": "#/$defs/TimestampPositionEnum", "default": "tl", "description": "Position of the timestamp on the image (tl/tr/bl/br).", "title": "Timestamp position"}, "format": {"default": "%m/%d/%Y %H:%M:%S", "description": "Datetime format string used for timestamps (Python datetime format codes).", "title": "Timestamp format", "type": "string"}, "color": {"$ref": "#/$defs/ColorConfig", "description": "RGB color values for the timestamp text (all values 0-255).", "title": "Timestamp color"}, "thickness": {"default": 2, "description": "Line thickness of the timestamp text.", "title": "Timestamp thickness", "type": "integer"}, "effect": {"anyOf": [{"$ref": "#/$defs/TimestampEffectEnum"}, {"type": "null"}], "default": null, "description": "Visual effect for the timestamp text (none, solid, shadow).", "title": "Timestamp effect"}}, "title": "TimestampStyleConfig", "type": "object"}, "TlsConfig": {"additionalProperties": false, "properties": {"enabled": {"default": true, "description": "Enable TLS for Frigate's web UI and API on the configured TLS port.", "title": "Enable TLS", "type": "boolean"}}, "title": "TlsConfig", "type": "object"}, "TriggerAction": {"enum": ["notification", "sub_label", "attribute"], "title": "TriggerAction", "type": "string"}, "TriggerConfig": {"additionalProperties": false, "properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional friendly name displayed in the UI for this trigger.", "title": "Friendly name"}, "enabled": {"default": true, "description": "Enable or disable this semantic search trigger.", "title": "Enable this trigger", "type": "boolean"}, "type": {"$ref": "#/$defs/TriggerType", "default": "description", "description": "Type of trigger: 'thumbnail' (match against image) or 'description' (match against text).", "title": "Trigger type"}, "data": {"description": "Text phrase or thumbnail ID to match against tracked objects.", "title": "Trigger content", "type": "string"}, "threshold": {"default": 0.8, "description": "Minimum similarity score (0-1) required to activate this trigger.", "exclusiveMinimum": 0.0, "maximum": 1.0, "title": "Trigger threshold", "type": "number"}, "actions": {"default": [], "description": "List of actions to execute when trigger matches (notification, sub_label, attribute).", "items": {"$ref": "#/$defs/TriggerAction"}, "title": "Trigger actions", "type": "array"}}, "required": ["data"], "title": "TriggerConfig", "type": "object"}, "TriggerType": {"enum": ["thumbnail", "description"], "title": "TriggerType", "type": "string"}, "UIConfig": {"additionalProperties": false, "properties": {"timezone": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional timezone to display across the UI (defaults to browser local time if unset).", "title": "Timezone"}, "time_format": {"$ref": "#/$defs/TimeFormatEnum", "default": "browser", "description": "Time format to use in the UI (browser, 12hour, or 24hour).", "title": "Time format"}, "unit_system": {"$ref": "#/$defs/UnitSystemEnum", "default": "metric", "description": "Unit system for display (metric or imperial) used in the UI and MQTT.", "title": "Unit system"}}, "title": "UIConfig", "type": "object"}, "UnitSystemEnum": {"enum": ["imperial", "metric"], "title": "UnitSystemEnum", "type": "string"}, "ZoneConfig": {"properties": {"friendly_name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "A user-friendly name for the zone, displayed in the Frigate UI. If not set, a formatted version of the zone name will be used.", "title": "Zone name"}, "enabled": {"default": true, "description": "Enable or disable this zone. Disabled zones are ignored at runtime.", "title": "Enabled", "type": "boolean"}, "enabled_in_config": {"anyOf": [{"type": "boolean"}, {"type": "null"}], "default": null, "title": "Keep track of original state of zone."}, "filters": {"additionalProperties": {"$ref": "#/$defs/FilterConfig"}, "description": "Filters to apply to objects within this zone. Used to reduce false positives or restrict which objects are considered present in the zone.", "title": "Zone filters", "type": "object"}, "coordinates": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "Polygon coordinates that define the zone area. Can be a comma-separated string or a list of coordinate strings. Coordinates should be relative (0-1) or absolute (legacy).", "title": "Coordinates"}, "distances": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}, {"type": "null"}], "description": "Optional real-world distances for each side of the zone quadrilateral, used for speed or distance calculations. Must have exactly 4 values if set.", "title": "Real-world distances"}, "inertia": {"default": 3, "description": "Number of consecutive frames an object must be detected in the zone before it is considered present. Helps filter out transient detections.", "exclusiveMinimum": 0, "title": "Inertia frames", "type": "integer"}, "loitering_time": {"default": 0, "description": "Number of seconds an object must remain in the zone to be considered as loitering. Set to 0 to disable loitering detection.", "minimum": 0, "title": "Loitering seconds", "type": "integer"}, "speed_threshold": {"anyOf": [{"minimum": 0.1, "type": "number"}, {"type": "null"}], "default": null, "description": "Minimum speed (in real-world units if distances are set) required for an object to be considered present in the zone. Used for speed-based zone triggers.", "title": "Minimum speed"}, "objects": {"anyOf": [{"type": "string"}, {"items": {"type": "string"}, "type": "array"}], "description": "List of object types (from labelmap) that can trigger this zone. Can be a string or a list of strings. If empty, all objects are considered.", "title": "Trigger objects"}}, "required": ["coordinates"], "title": "ZoneConfig", "type": "object"}, "ZoomingModeEnum": {"enum": ["disabled", "absolute", "relative"], "title": "ZoomingModeEnum", "type": "string"}}, "additionalProperties": false, "properties": {"version": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Numeric or string version of the active configuration to help detect migrations or format changes.", "title": "Current config version"}, "safe_mode": {"default": false, "description": "When enabled, start Frigate in safe mode with reduced features for troubleshooting.", "title": "Safe mode", "type": "boolean"}, "environment_vars": {"additionalProperties": {"type": "string"}, "description": "Key/value pairs of environment variables to set for the Frigate process in Home Assistant OS. Non-HAOS users must use Docker environment variable configuration instead.", "title": "Environment variables", "type": "object"}, "logger": {"$ref": "#/$defs/LoggerConfig", "description": "Controls default log verbosity and per-component log level overrides.", "title": "Logging"}, "auth": {"$ref": "#/$defs/AuthConfig", "description": "Authentication and session-related settings including cookie and rate limit options.", "title": "Authentication"}, "database": {"$ref": "#/$defs/DatabaseConfig", "description": "Settings for the SQLite database used by Frigate to store tracked object and recording metadata.", "title": "Database"}, "go2rtc": {"$ref": "#/$defs/RestreamConfig", "description": "Settings for the integrated go2rtc restreaming service used for live stream relaying and translation.", "title": "go2rtc"}, "mqtt": {"$ref": "#/$defs/MqttConfig", "description": "Settings for connecting and publishing telemetry, snapshots, and event details to an MQTT broker.", "title": "MQTT"}, "notifications": {"$ref": "#/$defs/NotificationConfig", "description": "Settings to enable and control notifications for all cameras; can be overridden per-camera.", "title": "Notifications"}, "networking": {"$ref": "#/$defs/NetworkingConfig", "description": "Network-related settings such as IPv6 enablement for Frigate endpoints.", "title": "Networking"}, "proxy": {"$ref": "#/$defs/ProxyConfig", "description": "Settings for integrating Frigate behind a reverse proxy that passes authenticated user headers.", "title": "Proxy"}, "telemetry": {"$ref": "#/$defs/TelemetryConfig", "description": "System telemetry and stats options including GPU and network bandwidth monitoring.", "title": "Telemetry"}, "tls": {"$ref": "#/$defs/TlsConfig", "description": "TLS settings for Frigate's web endpoints (port 8971).", "title": "TLS"}, "ui": {"$ref": "#/$defs/UIConfig", "description": "User interface preferences such as timezone, time/date formatting, and units.", "title": "UI"}, "detectors": {"additionalProperties": {"$ref": "#/$defs/BaseDetectorConfig"}, "default": {"cpu": {"type": "cpu"}}, "description": "Configuration for object detectors (CPU, GPU, ONNX backends) and any detector-specific model settings.", "title": "Detector hardware", "type": "object"}, "model": {"$ref": "#/$defs/ModelConfig", "description": "Settings to configure a custom object detection model and its input shape.", "title": "Detection model"}, "genai": {"additionalProperties": {"$ref": "#/$defs/GenAIConfig"}, "description": "Settings for integrated generative AI providers used to generate object descriptions and review summaries.", "title": "Generative AI configuration", "type": "object"}, "cameras": {"additionalProperties": {"$ref": "#/$defs/CameraConfig"}, "description": "Cameras", "title": "Cameras", "type": "object"}, "audio": {"$ref": "#/$defs/AudioConfig", "description": "Settings for audio-based event detection for all cameras; can be overridden per-camera.", "title": "Audio detection"}, "birdseye": {"$ref": "#/$defs/BirdseyeConfig", "description": "Settings for the Birdseye composite view that composes multiple camera feeds into a single layout.", "title": "Birdseye"}, "detect": {"$ref": "#/$defs/DetectConfig", "description": "Settings for the detection/detect role used to run object detection and initialize trackers.", "title": "Object Detection"}, "ffmpeg": {"$ref": "#/$defs/FfmpegConfig", "description": "FFmpeg settings including binary path, args, hwaccel options, and per-role output args.", "title": "FFmpeg"}, "live": {"$ref": "#/$defs/CameraLiveConfig", "description": "Settings to control the jsmpeg live stream resolution and quality. This does not affect restreamed cameras that use go2rtc for live view.", "title": "Live playback"}, "motion": {"anyOf": [{"$ref": "#/$defs/MotionConfig"}, {"type": "null"}], "default": null, "description": "Default motion detection settings applied to cameras unless overridden per-camera.", "title": "Motion detection"}, "objects": {"$ref": "#/$defs/ObjectConfig", "description": "Object tracking defaults including which labels to track and per-object filters.", "title": "Objects"}, "record": {"$ref": "#/$defs/RecordConfig", "description": "Recording and retention settings applied to cameras unless overridden per-camera.", "title": "Recording"}, "review": {"$ref": "#/$defs/ReviewConfig", "description": "Settings that control alerts, detections, and GenAI review summaries used by the UI and storage.", "title": "Review"}, "snapshots": {"$ref": "#/$defs/SnapshotsConfig", "description": "Settings for API-generated snapshots of tracked objects for all cameras; can be overridden per-camera.", "title": "Snapshots"}, "timestamp_style": {"$ref": "#/$defs/TimestampStyleConfig", "description": "Styling options for in-feed timestamps applied to debug view and snapshots.", "title": "Timestamp style"}, "audio_transcription": {"$ref": "#/$defs/AudioTranscriptionConfig", "description": "Settings for live and speech audio transcription used for events and live captions.", "title": "Audio transcription"}, "classification": {"$ref": "#/$defs/ClassificationConfig", "description": "Settings for classification models used to refine object labels or state classification.", "title": "Object classification"}, "semantic_search": {"$ref": "#/$defs/SemanticSearchConfig", "description": "Settings for Semantic Search which builds and queries object embeddings to find similar items.", "title": "Semantic Search"}, "face_recognition": {"$ref": "#/$defs/FaceRecognitionConfig", "description": "Settings for face detection and recognition for all cameras; can be overridden per-camera.", "title": "Face recognition"}, "lpr": {"$ref": "#/$defs/LicensePlateRecognitionConfig", "description": "License plate recognition settings including detection thresholds, formatting, and known plates.", "title": "License Plate Recognition"}, "camera_groups": {"additionalProperties": {"$ref": "#/$defs/CameraGroupConfig"}, "description": "Configuration for named camera groups used to organize cameras in the UI.", "title": "Camera groups", "type": "object"}, "profiles": {"additionalProperties": {"$ref": "#/$defs/ProfileDefinitionConfig"}, "description": "Named profile definitions with friendly names. Camera profiles must reference names defined here.", "title": "Profiles", "type": "object"}, "active_profile": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Currently active profile name. Runtime-only, not persisted in YAML.", "title": "Active profile"}}, "required": ["mqtt", "cameras"], "title": "FrigateConfig", "type": "object"} \ No newline at end of file diff --git a/web/e2e/fixtures/mock-data/config-snapshot.json b/web/e2e/fixtures/mock-data/config-snapshot.json index f8b5eb8f3b..478fc7d56d 100644 --- a/web/e2e/fixtures/mock-data/config-snapshot.json +++ b/web/e2e/fixtures/mock-data/config-snapshot.json @@ -1 +1 @@ -{"version": null, "safe_mode": false, "environment_vars": {}, "logger": {"default": "info", "logs": {}}, "auth": {"enabled": true, "reset_admin_password": false, "cookie_name": "frigate_token", "cookie_secure": false, "session_length": 86400, "refresh_time": 1800, "failed_login_rate_limit": null, "trusted_proxies": [], "hash_iterations": 600000, "roles": {"admin": [], "viewer": []}, "admin_first_time_login": false}, "database": {"path": "/config/frigate.db"}, "go2rtc": {}, "mqtt": {"enabled": true, "host": "mqtt", "port": 1883, "topic_prefix": "frigate", "client_id": "frigate", "stats_interval": 60, "user": null, "password": null, "tls_ca_certs": null, "tls_client_cert": null, "tls_client_key": null, "tls_insecure": null, "qos": 0}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "networking": {"ipv6": {"enabled": false}, "listen": {"internal": 5000, "external": 8971}}, "proxy": {"header_map": {"user": null, "role": null, "role_map": {}}, "logout_url": null, "auth_secret": null, "default_role": "viewer", "separator": ","}, "telemetry": {"network_interfaces": [], "stats": {"amd_gpu_stats": true, "intel_gpu_stats": true, "network_bandwidth": false, "intel_gpu_device": null}, "version_check": true}, "tls": {"enabled": true}, "ui": {"timezone": null, "time_format": "browser", "unit_system": "metric"}, "detectors": {"cpu": {"type": "cpu", "model": {"path": "/cpu_model.tflite", "labelmap_path": null, "width": 320, "height": 320, "labelmap": {}, "attributes_map": {"person": ["amazon", "face"], "car": ["amazon", "an_post", "canada_post", "dhl", "dpd", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "motorcycle": ["license_plate"]}, "input_tensor": "nhwc", "input_pixel_format": "rgb", "input_dtype": "int", "model_type": "ssd"}, "model_path": null}}, "model": {"path": null, "labelmap_path": null, "width": 320, "height": 320, "labelmap": {}, "attributes_map": {"person": ["amazon", "face"], "car": ["amazon", "an_post", "canada_post", "dhl", "dpd", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "motorcycle": ["license_plate"]}, "input_tensor": "nhwc", "input_pixel_format": "rgb", "input_dtype": "int", "model_type": "ssd", "all_attributes": ["amazon", "an_post", "canada_post", "dhl", "dpd", "face", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "colormap": {}}, "genai": {}, "cameras": {"front_door": {"name": "front_door", "friendly_name": null, "enabled": true, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": false, "num_threads": 2}, "audio_transcription": {"enabled": false, "enabled_in_config": false, "live_enabled": false}, "birdseye": {"enabled": true, "mode": {"continuous": false, "motion": false, "objects": true, "stationary_objects": false}, "order": 0}, "detect": {"enabled": false, "height": 720, "width": 1280, "fps": 5, "min_initialized": 2, "max_disappeared": 25, "stationary": {"interval": 50, "threshold": 50, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "face_recognition": {"enabled": false, "min_area": 750}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0, "inputs": [{"path": "rtsp://10.0.0.1:554/video", "roles": ["record", "detect"], "global_args": [], "hwaccel_args": [], "input_args": []}]}, "live": {"streams": {"front_door": "front_door"}, "height": 720, "quality": 8}, "lpr": {"enabled": false, "expire_time": 3, "min_area": 1000, "enhancement": 0}, "motion": {"enabled": true, "threshold": 30, "lightning_threshold": 0.8, "skip_motion_threshold": null, "improve_contrast": true, "contour_area": 10, "delta_alpha": 0.2, "frame_alpha": 0.01, "frame_height": 100, "mask": {}, "mqtt_off_delay": 30, "enabled_in_config": null}, "objects": {"track": ["person"], "filters": {"person": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.5, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": false}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": false}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": true, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": true}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": false, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "semantic_search": {"triggers": {}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "best_image_timeout": 60, "mqtt": {"enabled": true, "timestamp": true, "bounding_box": true, "crop": true, "height": 270, "required_zones": [], "quality": 70}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "onvif": {"host": "", "port": 8000, "user": null, "password": null, "tls_insecure": false, "profile": null, "autotracking": {"enabled": false, "calibrate_on_startup": false, "zooming": "disabled", "zoom_factor": 0.3, "track": ["person"], "required_zones": [], "return_preset": "home", "timeout": 10, "movement_weights": [], "enabled_in_config": false}, "ignore_time_mismatch": false}, "type": "generic", "ui": {"order": 0, "dashboard": true, "review": true}, "webui_url": null, "profiles": {}, "zones": {}, "enabled_in_config": true}, "backyard": {"name": "backyard", "friendly_name": null, "enabled": true, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": false, "num_threads": 2}, "audio_transcription": {"enabled": false, "enabled_in_config": false, "live_enabled": false}, "birdseye": {"enabled": true, "mode": {"continuous": false, "motion": false, "objects": true, "stationary_objects": false}, "order": 0}, "detect": {"enabled": false, "height": 720, "width": 1280, "fps": 5, "min_initialized": 2, "max_disappeared": 25, "stationary": {"interval": 50, "threshold": 50, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "face_recognition": {"enabled": false, "min_area": 750}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0, "inputs": [{"path": "rtsp://10.0.0.2:554/video", "roles": ["record", "detect"], "global_args": [], "hwaccel_args": [], "input_args": []}]}, "live": {"streams": {"backyard": "backyard"}, "height": 720, "quality": 8}, "lpr": {"enabled": false, "expire_time": 3, "min_area": 1000, "enhancement": 0}, "motion": {"enabled": true, "threshold": 30, "lightning_threshold": 0.8, "skip_motion_threshold": null, "improve_contrast": true, "contour_area": 10, "delta_alpha": 0.2, "frame_alpha": 0.01, "frame_height": 100, "mask": {}, "mqtt_off_delay": 30, "enabled_in_config": null}, "objects": {"track": ["person"], "filters": {"person": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.5, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": false}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": false}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": true, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": true}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": false, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "semantic_search": {"triggers": {}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "best_image_timeout": 60, "mqtt": {"enabled": true, "timestamp": true, "bounding_box": true, "crop": true, "height": 270, "required_zones": [], "quality": 70}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "onvif": {"host": "", "port": 8000, "user": null, "password": null, "tls_insecure": false, "profile": null, "autotracking": {"enabled": false, "calibrate_on_startup": false, "zooming": "disabled", "zoom_factor": 0.3, "track": ["person"], "required_zones": [], "return_preset": "home", "timeout": 10, "movement_weights": [], "enabled_in_config": false}, "ignore_time_mismatch": false}, "type": "generic", "ui": {"order": 0, "dashboard": true, "review": true}, "webui_url": null, "profiles": {}, "zones": {}, "enabled_in_config": true}, "garage": {"name": "garage", "friendly_name": null, "enabled": true, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": false, "num_threads": 2}, "audio_transcription": {"enabled": false, "enabled_in_config": false, "live_enabled": false}, "birdseye": {"enabled": true, "mode": {"continuous": false, "motion": false, "objects": true, "stationary_objects": false}, "order": 0}, "detect": {"enabled": false, "height": 720, "width": 1280, "fps": 5, "min_initialized": 2, "max_disappeared": 25, "stationary": {"interval": 50, "threshold": 50, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "face_recognition": {"enabled": false, "min_area": 750}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0, "inputs": [{"path": "rtsp://10.0.0.3:554/video", "roles": ["record", "detect"], "global_args": [], "hwaccel_args": [], "input_args": []}]}, "live": {"streams": {"garage": "garage"}, "height": 720, "quality": 8}, "lpr": {"enabled": false, "expire_time": 3, "min_area": 1000, "enhancement": 0}, "motion": {"enabled": true, "threshold": 30, "lightning_threshold": 0.8, "skip_motion_threshold": null, "improve_contrast": true, "contour_area": 10, "delta_alpha": 0.2, "frame_alpha": 0.01, "frame_height": 100, "mask": {}, "mqtt_off_delay": 30, "enabled_in_config": null}, "objects": {"track": ["person"], "filters": {"person": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.5, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": false}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": false}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": true, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": true}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": false, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "semantic_search": {"triggers": {}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "best_image_timeout": 60, "mqtt": {"enabled": true, "timestamp": true, "bounding_box": true, "crop": true, "height": 270, "required_zones": [], "quality": 70}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "onvif": {"host": "", "port": 8000, "user": null, "password": null, "tls_insecure": false, "profile": null, "autotracking": {"enabled": false, "calibrate_on_startup": false, "zooming": "disabled", "zoom_factor": 0.3, "track": ["person"], "required_zones": [], "return_preset": "home", "timeout": 10, "movement_weights": [], "enabled_in_config": false}, "ignore_time_mismatch": false}, "type": "generic", "ui": {"order": 0, "dashboard": true, "review": true}, "webui_url": null, "profiles": {}, "zones": {}, "enabled_in_config": true}}, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": null, "num_threads": 2}, "birdseye": {"enabled": true, "mode": {"continuous": false, "motion": false, "objects": true, "stationary_objects": false}, "restream": false, "width": 1280, "height": 720, "quality": 8, "inactivity_threshold": 30, "layout": {"scaling_factor": 2.0, "max_cameras": null}, "idle_heartbeat_fps": 0.0}, "detect": {"enabled": false, "height": null, "width": null, "fps": 5, "min_initialized": null, "max_disappeared": null, "stationary": {"interval": null, "threshold": null, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0}, "live": {"streams": [], "height": 720, "quality": 8}, "motion": null, "objects": {"track": ["person"], "filters": {"face": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "dhl": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "dpd": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "license_plate": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "postnord": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "nzpost": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "postnl": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "purolator": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "royal_mail": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "fedex": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "amazon": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "ups": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "canada_post": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "gls": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "usps": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "an_post": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": null}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": null}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": null, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": null}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": null, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "audio_transcription": {"enabled": false, "language": "en", "device": "CPU", "model_size": "small", "live_enabled": false}, "classification": {"bird": {"enabled": false, "threshold": 0.9}, "custom": {}}, "semantic_search": {"enabled": false, "reindex": false, "model": "jinav1", "model_size": "small", "device": null}, "face_recognition": {"enabled": false, "model_size": "small", "unknown_score": 0.8, "detection_threshold": 0.7, "recognition_threshold": 0.9, "min_area": 750, "min_faces": 1, "save_attempts": 200, "blur_confidence_filter": true, "device": null}, "lpr": {"enabled": false, "model_size": "small", "detection_threshold": 0.7, "min_area": 1000, "recognition_threshold": 0.9, "min_plate_length": 4, "format": null, "match_distance": 1, "known_plates": {}, "enhancement": 0, "debug_save_plates": false, "device": null, "replace_rules": []}, "camera_groups": {"default": {"cameras": ["front_door", "backyard", "garage"], "icon": "generic", "order": 0}, "outdoor": {"cameras": ["front_door", "backyard"], "icon": "generic", "order": 1}}, "profiles": {}} \ No newline at end of file +{"version": null, "safe_mode": false, "environment_vars": {}, "logger": {"default": "info", "logs": {}}, "auth": {"enabled": true, "reset_admin_password": false, "cookie_name": "frigate_token", "cookie_secure": false, "session_length": 86400, "refresh_time": 1800, "failed_login_rate_limit": null, "trusted_proxies": [], "hash_iterations": 600000, "roles": {"admin": [], "viewer": []}, "admin_first_time_login": false}, "database": {"path": "/config/frigate.db"}, "go2rtc": {}, "mqtt": {"enabled": true, "host": "mqtt", "port": 1883, "topic_prefix": "frigate", "client_id": "frigate", "stats_interval": 60, "user": null, "password": null, "tls_ca_certs": null, "tls_client_cert": null, "tls_client_key": null, "tls_insecure": null, "qos": 0}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "networking": {"ipv6": {"enabled": false}, "listen": {"internal": 5000, "external": 8971}}, "proxy": {"header_map": {"user": null, "role": null, "role_map": {}}, "logout_url": null, "auth_secret": null, "default_role": "viewer", "separator": ","}, "telemetry": {"network_interfaces": [], "stats": {"amd_gpu_stats": true, "intel_gpu_stats": true, "network_bandwidth": false, "intel_gpu_device": null}, "version_check": true}, "tls": {"enabled": true}, "ui": {"timezone": null, "time_format": "browser", "unit_system": "metric"}, "detectors": {"cpu": {"type": "cpu", "model": {"path": "/cpu_model.tflite", "labelmap_path": null, "width": 320, "height": 320, "labelmap": {}, "attributes_map": {"person": ["amazon", "face"], "car": ["amazon", "an_post", "canada_post", "dhl", "dpd", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "motorcycle": ["license_plate"]}, "input_tensor": "nhwc", "input_pixel_format": "rgb", "input_dtype": "int", "model_type": "ssd"}, "model_path": null}}, "model": {"path": null, "labelmap_path": null, "width": 320, "height": 320, "labelmap": {}, "attributes_map": {"person": ["amazon", "face"], "car": ["amazon", "an_post", "canada_post", "dhl", "dpd", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "motorcycle": ["license_plate"]}, "input_tensor": "nhwc", "input_pixel_format": "rgb", "input_dtype": "int", "model_type": "ssd", "all_attributes": ["amazon", "an_post", "canada_post", "dhl", "dpd", "face", "fedex", "gls", "license_plate", "nzpost", "postnl", "postnord", "purolator", "royal_mail", "ups", "usps"], "colormap": {}}, "genai": {}, "cameras": {"front_door": {"name": "front_door", "friendly_name": null, "enabled": true, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": false, "num_threads": 2}, "audio_transcription": {"enabled": false, "enabled_in_config": false, "live_enabled": false}, "birdseye": {"enabled": true, "modes": ["all_objects"], "order": 0}, "detect": {"enabled": false, "height": 720, "width": 1280, "fps": 5, "min_initialized": 2, "max_disappeared": 25, "stationary": {"interval": 50, "threshold": 50, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "face_recognition": {"enabled": false, "min_area": 750}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0, "inputs": [{"path": "rtsp://10.0.0.1:554/video", "roles": ["record", "detect"], "global_args": [], "hwaccel_args": [], "input_args": []}]}, "live": {"streams": {"front_door": "front_door"}, "height": 720, "quality": 8}, "lpr": {"enabled": false, "expire_time": 3, "min_area": 1000, "enhancement": 0}, "motion": {"enabled": true, "threshold": 30, "lightning_threshold": 0.8, "skip_motion_threshold": null, "improve_contrast": true, "contour_area": 10, "delta_alpha": 0.2, "frame_alpha": 0.01, "frame_height": 100, "mask": {}, "mqtt_off_delay": 30, "enabled_in_config": null}, "objects": {"track": ["person"], "filters": {"person": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.5, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": false}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": false}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": true, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": true}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": false, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "semantic_search": {"triggers": {}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "best_image_timeout": 60, "mqtt": {"enabled": true, "timestamp": true, "bounding_box": true, "crop": true, "height": 270, "required_zones": [], "quality": 70}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "onvif": {"host": "", "port": 8000, "user": null, "password": null, "tls_insecure": false, "profile": null, "autotracking": {"enabled": false, "calibrate_on_startup": false, "zooming": "disabled", "zoom_factor": 0.3, "track": ["person"], "required_zones": [], "return_preset": "home", "timeout": 10, "movement_weights": [], "enabled_in_config": false}, "ignore_time_mismatch": false}, "type": "generic", "ui": {"order": 0, "dashboard": true, "review": true}, "webui_url": null, "profiles": {}, "zones": {}, "enabled_in_config": true}, "backyard": {"name": "backyard", "friendly_name": null, "enabled": true, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": false, "num_threads": 2}, "audio_transcription": {"enabled": false, "enabled_in_config": false, "live_enabled": false}, "birdseye": {"enabled": true, "modes": ["all_objects"], "order": 0}, "detect": {"enabled": false, "height": 720, "width": 1280, "fps": 5, "min_initialized": 2, "max_disappeared": 25, "stationary": {"interval": 50, "threshold": 50, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "face_recognition": {"enabled": false, "min_area": 750}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0, "inputs": [{"path": "rtsp://10.0.0.2:554/video", "roles": ["record", "detect"], "global_args": [], "hwaccel_args": [], "input_args": []}]}, "live": {"streams": {"backyard": "backyard"}, "height": 720, "quality": 8}, "lpr": {"enabled": false, "expire_time": 3, "min_area": 1000, "enhancement": 0}, "motion": {"enabled": true, "threshold": 30, "lightning_threshold": 0.8, "skip_motion_threshold": null, "improve_contrast": true, "contour_area": 10, "delta_alpha": 0.2, "frame_alpha": 0.01, "frame_height": 100, "mask": {}, "mqtt_off_delay": 30, "enabled_in_config": null}, "objects": {"track": ["person"], "filters": {"person": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.5, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": false}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": false}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": true, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": true}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": false, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "semantic_search": {"triggers": {}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "best_image_timeout": 60, "mqtt": {"enabled": true, "timestamp": true, "bounding_box": true, "crop": true, "height": 270, "required_zones": [], "quality": 70}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "onvif": {"host": "", "port": 8000, "user": null, "password": null, "tls_insecure": false, "profile": null, "autotracking": {"enabled": false, "calibrate_on_startup": false, "zooming": "disabled", "zoom_factor": 0.3, "track": ["person"], "required_zones": [], "return_preset": "home", "timeout": 10, "movement_weights": [], "enabled_in_config": false}, "ignore_time_mismatch": false}, "type": "generic", "ui": {"order": 0, "dashboard": true, "review": true}, "webui_url": null, "profiles": {}, "zones": {}, "enabled_in_config": true}, "garage": {"name": "garage", "friendly_name": null, "enabled": true, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": false, "num_threads": 2}, "audio_transcription": {"enabled": false, "enabled_in_config": false, "live_enabled": false}, "birdseye": {"enabled": true, "modes": ["all_objects"], "order": 0}, "detect": {"enabled": false, "height": 720, "width": 1280, "fps": 5, "min_initialized": 2, "max_disappeared": 25, "stationary": {"interval": 50, "threshold": 50, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "face_recognition": {"enabled": false, "min_area": 750}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0, "inputs": [{"path": "rtsp://10.0.0.3:554/video", "roles": ["record", "detect"], "global_args": [], "hwaccel_args": [], "input_args": []}]}, "live": {"streams": {"garage": "garage"}, "height": 720, "quality": 8}, "lpr": {"enabled": false, "expire_time": 3, "min_area": 1000, "enhancement": 0}, "motion": {"enabled": true, "threshold": 30, "lightning_threshold": 0.8, "skip_motion_threshold": null, "improve_contrast": true, "contour_area": 10, "delta_alpha": 0.2, "frame_alpha": 0.01, "frame_height": 100, "mask": {}, "mqtt_off_delay": 30, "enabled_in_config": null}, "objects": {"track": ["person"], "filters": {"person": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.5, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": false}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": false}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": true, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": true}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": false, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "semantic_search": {"triggers": {}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "best_image_timeout": 60, "mqtt": {"enabled": true, "timestamp": true, "bounding_box": true, "crop": true, "height": 270, "required_zones": [], "quality": 70}, "notifications": {"enabled": false, "email": null, "cooldown": 0, "enabled_in_config": false}, "onvif": {"host": "", "port": 8000, "user": null, "password": null, "tls_insecure": false, "profile": null, "autotracking": {"enabled": false, "calibrate_on_startup": false, "zooming": "disabled", "zoom_factor": 0.3, "track": ["person"], "required_zones": [], "return_preset": "home", "timeout": 10, "movement_weights": [], "enabled_in_config": false}, "ignore_time_mismatch": false}, "type": "generic", "ui": {"order": 0, "dashboard": true, "review": true}, "webui_url": null, "profiles": {}, "zones": {}, "enabled_in_config": true}}, "audio": {"enabled": false, "max_not_heard": 30, "min_volume": 500, "listen": ["bark", "fire_alarm", "speech", "yell"], "filters": {"bark": {"threshold": 0.8}, "fire_alarm": {"threshold": 0.8}, "speech": {"threshold": 0.8}, "yell": {"threshold": 0.8}}, "enabled_in_config": null, "num_threads": 2}, "birdseye": {"enabled": true, "modes": ["all_objects"], "restream": false, "width": 1280, "height": 720, "quality": 8, "inactivity_threshold": 30, "layout": {"scaling_factor": 2.0, "max_cameras": null}, "idle_heartbeat_fps": 0.0}, "detect": {"enabled": false, "height": null, "width": null, "fps": 5, "min_initialized": null, "max_disappeared": null, "stationary": {"interval": null, "threshold": null, "max_frames": {"default": null, "objects": {}}, "classifier": true}, "annotation_offset": 0}, "ffmpeg": {"path": "default", "global_args": ["-hide_banner", "-loglevel", "warning", "-threads", "2"], "hwaccel_args": "preset-vaapi", "input_args": "preset-rtsp-generic", "output_args": {"detect": ["-threads", "2", "-f", "rawvideo", "-pix_fmt", "yuv420p"], "record": "preset-record-generic-audio-aac", "record_sub": []}, "retry_interval": 10.0, "apple_compatibility": false, "gpu": 0}, "live": {"streams": [], "height": 720, "quality": 8}, "motion": null, "objects": {"track": ["person"], "filters": {"dpd": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "nzpost": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "royal_mail": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "usps": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "dhl": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "fedex": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "license_plate": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "postnl": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "an_post": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "face": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "gls": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "amazon": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "canada_post": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "purolator": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "ups": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}, "postnord": {"min_area": 0, "max_area": 24000000, "min_ratio": 0, "max_ratio": 24000000, "threshold": 0.7, "min_score": 0.7, "mask": {}}}, "mask": {}, "genai": {"enabled": false, "use_snapshot": false, "prompt": "Analyze the sequence of images containing the {label}. Focus on the likely intent or behavior of the {label} based on its actions and movement, rather than describing its appearance or the surroundings. Consider what the {label} is doing, why, and what it might do next.", "object_prompts": {}, "objects": [], "required_zones": [], "debug_save_thumbnails": false, "send_triggers": {"tracked_object_end": true, "after_significant_updates": null}, "enabled_in_config": null}}, "record": {"enabled": false, "expire_interval": 60, "continuous": {"days": 0}, "motion": {"days": 0}, "detections": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "alerts": {"pre_capture": 5, "post_capture": 5, "retain": {"days": 10, "mode": "motion"}}, "export": {"hwaccel_args": "preset-vaapi", "max_concurrent": 3, "chapters": "review_items"}, "preview": {"quality": "medium"}, "sub": {"enabled": false, "continuous": {"days": 0}, "motion": {"days": 0}, "alerts": {"days": 10, "mode": "motion"}, "detections": {"days": 10, "mode": "motion"}}, "enabled_in_config": null}, "review": {"alerts": {"enabled": true, "labels": ["person", "car"], "required_zones": [], "enabled_in_config": null, "cutoff_time": 40}, "detections": {"enabled": true, "labels": null, "required_zones": [], "cutoff_time": 30, "enabled_in_config": null}, "genai": {"enabled": false, "alerts": true, "detections": false, "image_source": "preview", "additional_concerns": [], "debug_save_thumbnails": false, "enabled_in_config": null, "preferred_language": null, "activity_context_prompt": "### Normal Activity Indicators (Level 0)\n- Known/verified people in any zone at any time\n- People with pets in residential areas\n- Routine residential vehicle access during daytime/evening (6 AM - 10 PM): entering, exiting, loading/unloading items \u2014 normal commute and travel patterns\n- Deliveries or services during daytime/evening (6 AM - 10 PM): carrying packages to doors/porches, placing items, leaving\n- Services/maintenance workers with visible tools, uniforms, or service vehicles during daytime\n- Activity confined to public areas only (sidewalks, streets) without entering property at any time\n\n### Suspicious Activity Indicators (Level 1)\n- **Checking or probing vehicle/building access**: trying handles without entering, peering through windows, examining multiple vehicles, or possessing break-in tools \u2014 Level 1\n- **Unidentified person in private areas (driveways, near vehicles/buildings) during late night/early morning (11 PM - 5 AM)** \u2014 ALWAYS Level 1 regardless of activity or duration\n- Taking items that don't belong to them (packages, objects from porches/driveways)\n- Climbing or jumping fences/barriers to access property\n- Attempting to conceal actions or items from view\n- Prolonged loitering: remaining in same area without visible purpose throughout most of the sequence\n\n### Critical Threat Indicators (Level 2)\n- Holding break-in tools (crowbars, pry bars, bolt cutters)\n- Weapons visible (guns, knives, bats used aggressively)\n- Forced entry in progress\n- Physical aggression or violence\n- Active property damage or theft in progress\n\n### Assessment Guidance\nEvaluate in this order:\n\n1. **If person is verified/known** \u2192 Level 0 regardless of time or activity\n2. **If person is unidentified:**\n - Check time: If late night/early morning (11 PM - 5 AM) AND in private areas (driveways, near vehicles/buildings) \u2192 Level 1\n - Check actions: If probing access (trying handles without entering, checking multiple vehicles), taking items, climbing \u2192 Level 1\n - Otherwise, if daytime/evening (6 AM - 10 PM) with clear legitimate purpose (delivery, service, routine vehicle access) \u2192 Level 0\n3. **Escalate to Level 2 if:** Weapons, break-in tools, forced entry in progress, violence, or active property damage visible (escalates from Level 0 or 1)\n\nThe mere presence of an unidentified person in private areas during late night hours is inherently suspicious and warrants human review, regardless of what activity they appear to be doing or how brief the sequence is."}}, "snapshots": {"enabled": false, "timestamp": false, "bounding_box": true, "crop": false, "required_zones": [], "height": null, "retain": {"default": 10, "objects": {}}, "quality": 60}, "timestamp_style": {"position": "tl", "format": "%m/%d/%Y %H:%M:%S", "color": {"red": 255, "green": 255, "blue": 255}, "thickness": 2, "effect": null}, "audio_transcription": {"enabled": false, "language": "en", "device": "CPU", "model_size": "small", "live_enabled": false}, "classification": {"bird": {"enabled": false, "threshold": 0.9}, "custom": {}}, "semantic_search": {"enabled": false, "reindex": false, "model": "jinav1", "model_size": "small", "device": null}, "face_recognition": {"enabled": false, "model_size": "small", "unknown_score": 0.8, "detection_threshold": 0.7, "recognition_threshold": 0.9, "min_area": 750, "min_faces": 1, "save_attempts": 200, "blur_confidence_filter": true, "device": null}, "lpr": {"enabled": false, "model_size": "small", "detection_threshold": 0.7, "min_area": 1000, "recognition_threshold": 0.9, "min_plate_length": 4, "format": null, "match_distance": 1, "known_plates": {}, "enhancement": 0, "debug_save_plates": false, "device": null, "replace_rules": []}, "camera_groups": {"default": {"cameras": ["front_door", "backyard", "garage"], "icon": "generic", "order": 0}, "outdoor": {"cameras": ["front_door", "backyard"], "icon": "generic", "order": 1}}, "profiles": {}} \ No newline at end of file diff --git a/web/public/locales/en/config/cameras.json b/web/public/locales/en/config/cameras.json index 5f9d9249b7..ad07847cdd 100644 --- a/web/public/locales/en/config/cameras.json +++ b/web/public/locales/en/config/cameras.json @@ -70,25 +70,9 @@ "label": "Enable Birdseye", "description": "Enable or disable the Birdseye view feature." }, - "mode": { + "modes": { "label": "Activity types", - "description": "Activity types that include cameras in Birdseye.", - "continuous": { - "label": "Continuous", - "description": "Always include the camera in Birdseye." - }, - "motion": { - "label": "Motion", - "description": "Include the camera in Birdseye when motion is detected." - }, - "objects": { - "label": "Active objects", - "description": "Include the camera in Birdseye while an active object is tracked." - }, - "stationary_objects": { - "label": "Stationary objects", - "description": "Include the camera in Birdseye while a stationary object is tracked." - } + "description": "Activity types that include cameras in Birdseye." }, "order": { "label": "Position", diff --git a/web/public/locales/en/config/global.json b/web/public/locales/en/config/global.json index 058ea26fa4..3a7fe00c4c 100644 --- a/web/public/locales/en/config/global.json +++ b/web/public/locales/en/config/global.json @@ -557,25 +557,9 @@ "label": "Enable Birdseye", "description": "Enable or disable the Birdseye view feature." }, - "mode": { + "modes": { "label": "Activity types", - "description": "Activity types that include cameras in Birdseye.", - "continuous": { - "label": "Continuous", - "description": "Always include the camera in Birdseye." - }, - "motion": { - "label": "Motion", - "description": "Include the camera in Birdseye when motion is detected." - }, - "objects": { - "label": "Active objects", - "description": "Include the camera in Birdseye while an active object is tracked." - }, - "stationary_objects": { - "label": "Stationary objects", - "description": "Include the camera in Birdseye while a stationary object is tracked." - } + "description": "Activity types that include cameras in Birdseye." }, "restream": { "label": "Restream RTSP", diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index 124c0ff5f4..bb8927207e 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -1657,6 +1657,16 @@ "summary": "{{count}} selected", "empty": "No zones available" }, + "birdseyeModes": { + "summary": "{{count}} selected", + "options": { + "continuous": "Continuous", + "motion": "Motion", + "all_objects": "All objects", + "alerts": "Alerts", + "detections": "Detections" + } + }, "inputRoles": { "summary": "{{count}} roles selected", "empty": "No roles available", @@ -1856,11 +1866,6 @@ } }, "birdseye": { - "trackingMode": { - "objects": "Objects", - "motion": "Motion", - "continuous": "Continuous" - }, "cameraOrder": { "label": "Camera order", "description": "Drag cameras to set their order in the Birdseye layout.", diff --git a/web/src/components/config-form/section-configs/birdseye.ts b/web/src/components/config-form/section-configs/birdseye.ts index 0abbd1c17f..1965e027cb 100644 --- a/web/src/components/config-form/section-configs/birdseye.ts +++ b/web/src/components/config-form/section-configs/birdseye.ts @@ -10,29 +10,26 @@ const birdseye: SectionConfigOverrides = { severity: "info", condition: (ctx) => { if (ctx.level !== "camera" || !ctx.fullCameraConfig) return false; - const mode = ctx.formData?.mode; - if (!mode || typeof mode !== "object" || Array.isArray(mode)) { + const modes = ctx.formData?.modes; + if (!Array.isArray(modes)) { return false; } return ( - (mode.objects === true || mode.stationary_objects === true) && + modes.includes("all_objects") && ctx.fullCameraConfig.detect?.enabled === false ); }, }, ], restartRequired: [], - fieldOrder: ["enabled", "mode", "order"], + fieldOrder: ["enabled", "modes", "order"], hiddenFields: ["order"], advancedFields: [], - overrideFields: ["enabled", "mode"], + overrideFields: ["enabled", "modes"], uiSchema: { - mode: { - continuous: { "ui:size": "xs" }, - motion: { "ui:size": "xs" }, - objects: { "ui:size": "xs" }, - stationary_objects: { "ui:size": "xs" }, + modes: { + "ui:widget": "birdseyeModes", }, }, }, @@ -43,7 +40,7 @@ const birdseye: SectionConfigOverrides = { "width", "height", "quality", - "mode", + "modes", "layout", "inactivity_threshold", "idle_heartbeat_fps", @@ -59,7 +56,7 @@ const birdseye: SectionConfigOverrides = { "idle_heartbeat_fps", ], uiSchema: { - mode: { + modes: { "ui:after": { render: "BirdseyeCameraReorder" }, }, }, diff --git a/web/src/components/config-form/theme/frigateTheme.ts b/web/src/components/config-form/theme/frigateTheme.ts index 109c44eb9a..6d2bc27451 100644 --- a/web/src/components/config-form/theme/frigateTheme.ts +++ b/web/src/components/config-form/theme/frigateTheme.ts @@ -22,6 +22,7 @@ import { ObjectLabelSwitchesWidget } from "./widgets/ObjectLabelSwitchesWidget"; import { AudioLabelSwitchesWidget } from "./widgets/AudioLabelSwitchesWidget"; import { ReviewLabelSwitchesWidget } from "./widgets/ReviewLabelSwitchesWidget"; import { ZoneSwitchesWidget } from "./widgets/ZoneSwitchesWidget"; +import { BirdseyeModesWidget } from "./widgets/BirdseyeModesWidget"; import { ArrayAsTextWidget } from "./widgets/ArrayAsTextWidget"; import { FfmpegArgsWidget } from "./widgets/FfmpegArgsWidget"; import { GenAIModelWidget } from "./widgets/GenAIModelWidget"; @@ -87,6 +88,7 @@ export const frigateTheme: FrigateTheme = { audioLabels: AudioLabelSwitchesWidget, reviewLabels: ReviewLabelSwitchesWidget, zoneNames: ZoneSwitchesWidget, + birdseyeModes: BirdseyeModesWidget, timezoneSelect: TimezoneSelectWidget, optionalField: OptionalFieldWidget, semanticSearchModel: SemanticSearchModelWidget, diff --git a/web/src/components/config-form/theme/widgets/BirdseyeModesWidget.tsx b/web/src/components/config-form/theme/widgets/BirdseyeModesWidget.tsx new file mode 100644 index 0000000000..80d0c24690 --- /dev/null +++ b/web/src/components/config-form/theme/widgets/BirdseyeModesWidget.tsx @@ -0,0 +1,44 @@ +// Birdseye Modes Widget - For selecting Birdseye activity types via switches +import type { WidgetProps } from "@rjsf/utils"; +import { SwitchesWidget } from "./SwitchesWidget"; +import type { FormContext } from "./SwitchesWidget"; + +// order matches BirdseyeModeEnum in frigate/config/camera/birdseye.py +const BIRDSEYE_MODES: string[] = [ + "continuous", + "motion", + "all_objects", + "alerts", + "detections", +]; + +function getBirdseyeModes(): string[] { + return BIRDSEYE_MODES; +} + +function getBirdseyeModeLabel(mode: string, context?: FormContext): string { + const t = context?.t; + + if (!t) { + return mode; + } + + return t(`configForm.birdseyeModes.options.${mode}`, { + ns: "views/settings", + defaultValue: mode, + }); +} + +export function BirdseyeModesWidget(props: WidgetProps) { + return ( + + ); +} diff --git a/web/src/hooks/use-config-override.ts b/web/src/hooks/use-config-override.ts index 8b4e4d2da9..3ed83ddeb3 100644 --- a/web/src/hooks/use-config-override.ts +++ b/web/src/hooks/use-config-override.ts @@ -480,7 +480,7 @@ export const OVERRIDABLE_SECTIONS: ReadonlyArray<{ key: "audio_transcription", compareFields: ["enabled", "live_enabled"], }, - { key: "birdseye", compareFields: ["enabled", "mode"] }, + { key: "birdseye", compareFields: ["enabled", "modes"] }, { key: "face_recognition", compareFields: ["enabled", "min_area"] }, { key: "ffmpeg", diff --git a/web/src/types/frigateConfig.ts b/web/src/types/frigateConfig.ts index e554d10e6f..cf5f8ffb0a 100644 --- a/web/src/types/frigateConfig.ts +++ b/web/src/types/frigateConfig.ts @@ -13,18 +13,18 @@ export interface UiConfig { export interface BirdseyeConfig { enabled: boolean; height: number; - mode: BirdseyeModeConfig; + modes: BirdseyeMode[]; quality: number; restream: boolean; width: number; } -export interface BirdseyeModeConfig { - continuous: boolean; - motion: boolean; - objects: boolean; - stationary_objects: boolean; -} +export type BirdseyeMode = + | "continuous" + | "motion" + | "all_objects" + | "alerts" + | "detections"; export interface FaceRecognitionConfig { enabled: boolean; @@ -56,7 +56,7 @@ export interface CameraConfig { best_image_timeout: number; birdseye: { enabled: boolean; - mode: BirdseyeModeConfig; + modes: BirdseyeMode[]; order: number; }; detect: {