Increase ruff coverage (#23644)
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
CI / Jetson Jetpack 6 (push) Waiting to run

* Pin ruff

* Add python upgrade fixes

This enables python upgrade checks in ruff to look for deprecated types and patterns. This namely fixes:
- usage of deprecated `Typing` which is now built in
- some specific exceptions which are caught and have new aliases

Some specific UP checks were also ignored as they are stylistic / unimportant and likely to cause bugs

* Remove async blocking calls

Use asyncio.to_thread on two remaining blocking calls to fix hanging event thread loop. Enable this specific rule to block it in the future.

* Use proper logging mechanism

* Correctly format logs

* Raise with context

When raising an exception include the from context to improve debugging

* Cleanup
This commit is contained in:
Nicolas Mowen
2026-07-06 12:28:02 -05:00
committed by GitHub
parent 455b8687e8
commit 4ee12e6237
169 changed files with 1053 additions and 1150 deletions
+4 -6
View File
@@ -1,5 +1,3 @@
from typing import Dict, List, Optional
from pydantic import Field, field_validator, model_validator
from .base import FrigateBaseModel
@@ -41,7 +39,7 @@ class AuthConfig(FrigateBaseModel):
description="When a session is within this many seconds of expiring, refresh it back to full length.",
ge=30,
)
failed_login_rate_limit: Optional[str] = Field(
failed_login_rate_limit: str | None = Field(
default=None,
title="Failed login limits",
description="Rate limiting rules for failed login attempts to reduce brute-force attacks.",
@@ -57,12 +55,12 @@ class AuthConfig(FrigateBaseModel):
title="Hash iterations",
description="Number of PBKDF2-SHA256 iterations to use when hashing user passwords.",
)
roles: Dict[str, List[str]] = Field(
roles: dict[str, list[str]] = Field(
default_factory=dict,
title="Role mappings",
description="Map roles to camera lists. An empty list grants access to all cameras for the role.",
)
admin_first_time_login: Optional[bool] = Field(
admin_first_time_login: bool | None = Field(
default=False,
title="First-time admin flag",
description=(
@@ -72,7 +70,7 @@ class AuthConfig(FrigateBaseModel):
@field_validator("roles")
@classmethod
def validate_roles(cls, v: Dict[str, List[str]]) -> Dict[str, List[str]]:
def validate_roles(cls, v: dict[str, list[str]]) -> dict[str, list[str]]:
# Ensure role names are valid (alphanumeric with underscores)
for role in v.keys():
if not role.replace("_", "").isalnum():
+2 -4
View File
@@ -1,5 +1,3 @@
from typing import Optional
from pydantic import Field
from frigate.const import AUDIO_MIN_CONFIDENCE
@@ -43,12 +41,12 @@ class AudioConfig(FrigateBaseModel):
title="Listen types",
description="List of audio event types to detect (for example: bark, fire_alarm, speech, yell).",
)
filters: Optional[dict[str, AudioFilterConfig]] = Field(
filters: dict[str, AudioFilterConfig] | None = Field(
None,
title="Audio filters",
description="Per-audio-type filter settings such as confidence thresholds used to reduce false positives.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
None,
title="Original audio state",
description="Indicates whether audio detection was originally enabled in the static config file.",
+1 -2
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field
@@ -35,7 +34,7 @@ class BirdseyeLayoutConfig(FrigateBaseModel):
ge=1.0,
le=5.0,
)
max_cameras: Optional[int] = Field(
max_cameras: int | None = Field(
default=None,
title="Max cameras",
description="Maximum number of cameras to display at once in Birdseye; shows the most recent cameras.",
+4 -5
View File
@@ -1,6 +1,5 @@
import os
from enum import Enum
from typing import Optional
from pydantic import Field, PrivateAttr, model_validator
@@ -51,14 +50,14 @@ class CameraTypeEnum(str, Enum):
class CameraConfig(FrigateBaseModel):
name: Optional[str] = Field(
name: str | None = Field(
None,
title="Camera name",
description="Camera name is required",
pattern=REGEX_CAMERA_NAME,
)
friendly_name: Optional[str] = Field(
friendly_name: str | None = Field(
None,
title="Friendly name",
description="Camera friendly name used in the Frigate UI",
@@ -180,7 +179,7 @@ class CameraConfig(FrigateBaseModel):
title="Camera UI",
description="Display ordering and visibility for this camera in the UI. Ordering affects the default dashboard. For more granular control, use camera groups.",
)
webui_url: Optional[str] = Field(
webui_url: str | None = Field(
None,
title="Camera URL",
description="URL to visit the camera directly from system page",
@@ -196,7 +195,7 @@ class CameraConfig(FrigateBaseModel):
title="Zones",
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.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original camera state",
description="Keep track of original state of camera.",
+7 -9
View File
@@ -1,5 +1,3 @@
from typing import Optional
from pydantic import Field, model_validator
from ..base import FrigateBaseModel
@@ -8,7 +6,7 @@ __all__ = ["DetectConfig", "StationaryConfig", "StationaryMaxFramesConfig"]
class StationaryMaxFramesConfig(FrigateBaseModel):
default: Optional[int] = Field(
default: int | None = Field(
default=None,
title="Default max frames",
description="Default maximum frames to track a stationary object before stopping.",
@@ -22,13 +20,13 @@ class StationaryMaxFramesConfig(FrigateBaseModel):
class StationaryConfig(FrigateBaseModel):
interval: Optional[int] = Field(
interval: int | None = Field(
default=None,
title="Stationary interval",
description="How often (in frames) to run a detection check to confirm a stationary object.",
gt=0,
)
threshold: Optional[int] = Field(
threshold: int | None = Field(
default=None,
title="Stationary threshold",
description="Number of frames with no position change required to mark an object as stationary.",
@@ -52,12 +50,12 @@ class DetectConfig(FrigateBaseModel):
title="Enable object detection",
description="Enable or disable object detection for all cameras; can be overridden per-camera.",
)
height: Optional[int] = Field(
height: int | None = Field(
default=None,
title="Detect height",
description="Height (pixels) of frames used for the detect stream; leave empty to use the native stream resolution.",
)
width: Optional[int] = Field(
width: int | None = Field(
default=None,
title="Detect width",
description="Width (pixels) of frames used for the detect stream; leave empty to use the native stream resolution.",
@@ -67,13 +65,13 @@ class DetectConfig(FrigateBaseModel):
title="Detect FPS",
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).",
)
min_initialized: Optional[int] = Field(
min_initialized: int | None = Field(
default=None,
title="Minimum initialization frames",
description="Number of consecutive detection hits required before creating a tracked object. Increase to reduce false initializations. Default value is fps divided by 2.",
ge=2,
)
max_disappeared: Optional[int] = Field(
max_disappeared: int | None = Field(
default=None,
title="Maximum disappeared frames",
description="Number of frames without a detection before a tracked object is considered gone.",
+8 -9
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Union
from pydantic import Field, field_validator
@@ -33,12 +32,12 @@ DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT = [
class FfmpegOutputArgsConfig(FrigateBaseModel):
detect: Union[str, list[str]] = Field(
detect: str | list[str] = Field(
default=DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT,
title="Detect output arguments",
description="Default output arguments for detect role streams.",
)
record: Union[str, list[str]] = Field(
record: str | list[str] = Field(
default=RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT,
title="Record output arguments",
description="Default output arguments for record role streams.",
@@ -51,17 +50,17 @@ class FfmpegConfig(FrigateBaseModel):
title="FFmpeg path",
description='Path to the FFmpeg binary to use or a version alias ("7.0" or "8.0").',
)
global_args: Union[str, list[str]] = Field(
global_args: str | list[str] = Field(
default=FFMPEG_GLOBAL_ARGS_DEFAULT,
title="FFmpeg global arguments",
description="Global arguments passed to FFmpeg processes.",
)
hwaccel_args: Union[str, list[str]] = Field(
hwaccel_args: str | list[str] = Field(
default="auto",
title="Hardware acceleration arguments",
description="Hardware acceleration arguments for FFmpeg. Provider-specific presets are recommended.",
)
input_args: Union[str, list[str]] = Field(
input_args: str | list[str] = Field(
default=FFMPEG_INPUT_ARGS_DEFAULT,
title="Input arguments",
description="Input arguments applied to FFmpeg input streams.",
@@ -112,17 +111,17 @@ class CameraInput(FrigateBaseModel):
title="Input roles",
description="Roles for this input stream.",
)
global_args: Union[str, list[str]] = Field(
global_args: str | list[str] = Field(
default_factory=list,
title="FFmpeg global arguments",
description="FFmpeg global arguments for this input stream.",
)
hwaccel_args: Union[str, list[str]] = Field(
hwaccel_args: str | list[str] = Field(
default_factory=list,
title="Hardware acceleration arguments",
description="Hardware acceleration arguments for this input stream.",
)
input_args: Union[str, list[str]] = Field(
input_args: str | list[str] = Field(
default_factory=list,
title="Input arguments",
description="Input arguments specific to this stream.",
+3 -3
View File
@@ -1,5 +1,5 @@
from enum import Enum
from typing import Any, Optional
from typing import Any
from pydantic import Field
@@ -26,12 +26,12 @@ class GenAIRoleEnum(str, Enum):
class GenAIConfig(FrigateBaseModel):
"""Primary GenAI Config to define GenAI Provider."""
api_key: Optional[EnvString] = Field(
api_key: EnvString | None = Field(
default=None,
title="API key",
description="API key required by some providers (can also be set via environment variables).",
)
base_url: Optional[str] = Field(
base_url: str | None = Field(
default=None,
title="Base URL",
description="Base URL for self-hosted or compatible providers (for example an Ollama instance).",
+1 -3
View File
@@ -1,5 +1,3 @@
from typing import Dict
from pydantic import Field
from ..base import FrigateBaseModel
@@ -8,7 +6,7 @@ __all__ = ["CameraLiveConfig"]
class CameraLiveConfig(FrigateBaseModel):
streams: Dict[str, str] = Field(
streams: dict[str, str] = Field(
default_factory=list,
title="Live stream names",
description="Mapping of configured stream names to restream/go2rtc names used for live playback.",
+9 -9
View File
@@ -1,6 +1,6 @@
"""Mask configuration for motion and object masks."""
from typing import Any, Optional, Union
from typing import Any
from pydantic import Field, field_serializer
@@ -12,7 +12,7 @@ __all__ = ["MotionMaskConfig", "ObjectMaskConfig"]
class MotionMaskConfig(FrigateBaseModel):
"""Configuration for a single motion mask."""
friendly_name: Optional[str] = Field(
friendly_name: str | None = Field(
default=None,
title="Friendly name",
description="A friendly name for this motion mask used in the Frigate UI",
@@ -22,13 +22,13 @@ class MotionMaskConfig(FrigateBaseModel):
title="Enabled",
description="Enable or disable this motion mask",
)
coordinates: Union[str, list[str]] = Field(
coordinates: str | list[str] = Field(
default="",
title="Coordinates",
description="Ordered x,y coordinates defining the motion mask polygon used to include/exclude areas.",
)
raw_coordinates: Union[str, list[str]] = ""
enabled_in_config: Optional[bool] = Field(
raw_coordinates: str | list[str] = ""
enabled_in_config: bool | None = Field(
default=None, title="Keep track of original state of motion mask."
)
@@ -50,7 +50,7 @@ class MotionMaskConfig(FrigateBaseModel):
class ObjectMaskConfig(FrigateBaseModel):
"""Configuration for a single object mask."""
friendly_name: Optional[str] = Field(
friendly_name: str | None = Field(
default=None,
title="Friendly name",
description="A friendly name for this object mask used in the Frigate UI",
@@ -60,13 +60,13 @@ class ObjectMaskConfig(FrigateBaseModel):
title="Enabled",
description="Enable or disable this object mask",
)
coordinates: Union[str, list[str]] = Field(
coordinates: str | list[str] = Field(
default="",
title="Coordinates",
description="Ordered x,y coordinates defining the object mask polygon used to include/exclude areas.",
)
raw_coordinates: Union[str, list[str]] = ""
enabled_in_config: Optional[bool] = Field(
raw_coordinates: str | list[str] = ""
enabled_in_config: bool | None = Field(
default=None, title="Keep track of original state of object mask."
)
+7 -7
View File
@@ -1,4 +1,4 @@
from typing import Any, Optional
from typing import Any
from pydantic import Field, field_serializer
@@ -28,7 +28,7 @@ class MotionConfig(FrigateBaseModel):
ge=0.3,
le=1.0,
)
skip_motion_threshold: Optional[float] = Field(
skip_motion_threshold: float | None = Field(
default=None,
title="Skip motion threshold",
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‑tracking an object. The trade‑off is between dropping a few megabytes of recordings versus reviewing a couple short clips. Leave unset (None) to disable this feature.",
@@ -40,7 +40,7 @@ class MotionConfig(FrigateBaseModel):
title="Improve contrast",
description="Apply contrast improvement to frames before motion analysis to help detection.",
)
contour_area: Optional[int] = Field(
contour_area: int | None = Field(
default=10,
title="Contour area",
description="Minimum contour area in pixels required for a motion contour to be counted.",
@@ -55,12 +55,12 @@ class MotionConfig(FrigateBaseModel):
title="Frame alpha",
description="Alpha value used when blending frames for motion preprocessing.",
)
frame_height: Optional[int] = Field(
frame_height: int | None = Field(
default=100,
title="Frame height",
description="Height in pixels to scale frames to when computing motion.",
)
mask: dict[str, Optional[MotionMaskConfig]] = Field(
mask: dict[str, MotionMaskConfig | None] = Field(
default_factory=dict,
title="Mask coordinates",
description="Ordered x,y coordinates defining the motion mask polygon used to include/exclude areas.",
@@ -70,12 +70,12 @@ class MotionConfig(FrigateBaseModel):
title="MQTT off delay",
description="Seconds to wait after last motion before publishing an MQTT 'off' state.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original motion state",
description="Indicates whether motion detection was enabled in the original static configuration.",
)
raw_mask: dict[str, Optional[MotionMaskConfig]] = Field(
raw_mask: dict[str, MotionMaskConfig | None] = Field(
default_factory=dict, exclude=True
)
+2 -4
View File
@@ -1,5 +1,3 @@
from typing import Optional
from pydantic import Field
from ..base import FrigateBaseModel
@@ -13,7 +11,7 @@ class NotificationConfig(FrigateBaseModel):
title="Enable notifications",
description="Enable or disable notifications for all cameras; can be overridden per-camera.",
)
email: Optional[str] = Field(
email: str | None = Field(
default=None,
title="Notification email",
description="Email address used for push notifications or required by certain notification providers.",
@@ -24,7 +22,7 @@ class NotificationConfig(FrigateBaseModel):
title="Cooldown period",
description="Cooldown (seconds) between notifications to avoid spamming recipients.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original notifications state",
description="Indicates whether notifications were enabled in the original static configuration.",
+11 -11
View File
@@ -1,4 +1,4 @@
from typing import Any, Optional, Union
from typing import Any
from pydantic import Field, PrivateAttr, field_serializer, field_validator
@@ -12,12 +12,12 @@ DEFAULT_TRACKED_OBJECTS = ["person"]
class FilterConfig(FrigateBaseModel):
min_area: Union[int, float] = Field(
min_area: int | float = Field(
default=0,
title="Minimum object area",
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).",
)
max_area: Union[int, float] = Field(
max_area: int | float = Field(
default=24000000,
title="Maximum object area",
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).",
@@ -42,12 +42,12 @@ class FilterConfig(FrigateBaseModel):
title="Minimum confidence",
description="Minimum single-frame detection confidence required for the object to be counted.",
)
mask: dict[str, Optional[ObjectMaskConfig]] = Field(
mask: dict[str, ObjectMaskConfig | None] = Field(
default_factory=dict,
title="Filter mask",
description="Polygon coordinates defining where this filter applies within the frame.",
)
raw_mask: dict[str, Optional[ObjectMaskConfig]] = Field(
raw_mask: dict[str, ObjectMaskConfig | None] = Field(
default_factory=dict, exclude=True
)
@@ -68,7 +68,7 @@ class GenAIObjectTriggerConfig(FrigateBaseModel):
title="Send on end",
description="Send a request to GenAI when the tracked object ends.",
)
after_significant_updates: Optional[int] = Field(
after_significant_updates: int | None = Field(
default=None,
title="Early GenAI trigger",
description="Send a request to GenAI after a specified number of significant updates for the tracked object.",
@@ -98,12 +98,12 @@ class GenAIObjectConfig(FrigateBaseModel):
description="Per-object prompts to customize GenAI outputs for specific labels.",
)
objects: Union[str, list[str]] = Field(
objects: str | list[str] = Field(
default_factory=list,
title="GenAI objects",
description="List of object labels to send to GenAI by default.",
)
required_zones: Union[str, list[str]] = Field(
required_zones: str | list[str] = Field(
default_factory=list,
title="Required zones",
description="Zones that must be entered for objects to qualify for GenAI description generation.",
@@ -118,7 +118,7 @@ class GenAIObjectConfig(FrigateBaseModel):
title="GenAI triggers",
description="Defines when frames should be sent to GenAI (on end, after updates, etc.).",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original GenAI state",
description="Indicates whether GenAI was enabled in the original static config.",
@@ -144,12 +144,12 @@ class ObjectConfig(FrigateBaseModel):
title="Object filters",
description="Filters applied to detected objects to reduce false positives (area, ratio, confidence).",
)
mask: dict[str, Optional[ObjectMaskConfig]] = Field(
mask: dict[str, ObjectMaskConfig | None] = Field(
default_factory=dict,
title="Object mask",
description="Mask polygon used to prevent object detection in specified areas.",
)
raw_mask: dict[str, Optional[ObjectMaskConfig]] = Field(
raw_mask: dict[str, ObjectMaskConfig | None] = Field(
default_factory=dict, exclude=True
)
genai: GenAIObjectConfig = Field(
+5 -6
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Optional, Union
from pydantic import Field, field_validator
@@ -59,12 +58,12 @@ class PtzAutotrackConfig(FrigateBaseModel):
title="Return timeout",
description="Wait this many seconds after losing tracking before returning camera to preset position.",
)
movement_weights: Optional[Union[str, list[str]]] = Field(
movement_weights: str | list[str] | None = Field(
default_factory=list,
title="Movement weights",
description="Calibration values automatically generated by camera calibration. Do not modify manually.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original autotrack state",
description="Internal field to track whether autotracking was enabled in configuration.",
@@ -102,12 +101,12 @@ class OnvifConfig(FrigateBaseModel):
title="ONVIF port",
description="Port number for the ONVIF service.",
)
user: Optional[EnvString] = Field(
user: EnvString | None = Field(
default=None,
title="ONVIF username",
description="Username for ONVIF authentication; some devices require admin user for ONVIF.",
)
password: Optional[EnvString] = Field(
password: EnvString | None = Field(
default=None,
title="ONVIF password",
description="Password for ONVIF authentication.",
@@ -117,7 +116,7 @@ class OnvifConfig(FrigateBaseModel):
title="Disable TLS verify",
description="Skip TLS verification and disable digest auth for ONVIF (unsafe; use in safe networks only).",
)
profile: Optional[str] = Field(
profile: str | None = Field(
default=None,
title="ONVIF profile",
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.",
+13 -15
View File
@@ -1,7 +1,5 @@
"""Camera profile configuration for named config overrides."""
from typing import Optional
from ..base import FrigateBaseModel
from ..classification import (
CameraFaceRecognitionConfig,
@@ -29,16 +27,16 @@ class CameraProfileConfig(FrigateBaseModel):
explicitly-set fields are used as overrides via exclude_unset.
"""
enabled: Optional[bool] = None
audio: Optional[AudioConfig] = None
birdseye: Optional[BirdseyeCameraConfig] = None
detect: Optional[DetectConfig] = None
face_recognition: Optional[CameraFaceRecognitionConfig] = None
lpr: Optional[CameraLicensePlateRecognitionConfig] = None
motion: Optional[MotionConfig] = None
notifications: Optional[NotificationConfig] = None
objects: Optional[ObjectConfig] = None
record: Optional[RecordConfig] = None
review: Optional[ReviewConfig] = None
snapshots: Optional[SnapshotsConfig] = None
zones: Optional[dict[str, ZoneConfig]] = None
enabled: bool | None = None
audio: AudioConfig | None = None
birdseye: BirdseyeCameraConfig | None = None
detect: DetectConfig | None = None
face_recognition: CameraFaceRecognitionConfig | None = None
lpr: CameraLicensePlateRecognitionConfig | None = None
motion: MotionConfig | None = None
notifications: NotificationConfig | None = None
objects: ObjectConfig | None = None
record: RecordConfig | None = None
review: ReviewConfig | None = None
snapshots: SnapshotsConfig | None = None
zones: dict[str, ZoneConfig] | None = None
+2 -3
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Optional, Union
from pydantic import Field
@@ -94,7 +93,7 @@ class ChaptersEnum(str, Enum):
class RecordExportConfig(FrigateBaseModel):
hwaccel_args: Union[str, list[str]] = Field(
hwaccel_args: str | list[str] = Field(
default="auto",
title="Export hwaccel args",
description="Hardware acceleration args to use for export/transcode operations.",
@@ -152,7 +151,7 @@ class RecordConfig(FrigateBaseModel):
title="Preview config",
description="Settings controlling the quality of recording previews shown in the UI.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original recording state",
description="Indicates whether recording was enabled in the original static configuration.",
+6 -7
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Optional, Union
from pydantic import Field, field_validator
@@ -32,13 +31,13 @@ class AlertsConfig(FrigateBaseModel):
title="Alert labels",
description="List of object labels that qualify as alerts (for example: car, person).",
)
required_zones: Union[str, list[str]] = Field(
required_zones: str | list[str] = Field(
default_factory=list,
title="Required zones",
description="Zones that an object must enter to be considered an alert; leave empty to allow any zone.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original alerts state",
description="Tracks whether alerts were originally enabled in the static configuration.",
@@ -67,12 +66,12 @@ class DetectionsConfig(FrigateBaseModel):
description="Enable or disable detection events for all cameras; can be overridden per-camera.",
)
labels: Optional[list[str]] = Field(
labels: list[str] | None = Field(
default=None,
title="Detection labels",
description="List of object labels that qualify as detection events.",
)
required_zones: Union[str, list[str]] = Field(
required_zones: str | list[str] = Field(
default_factory=list,
title="Required zones",
description="Zones that an object must enter to be considered a detection; leave empty to allow any zone.",
@@ -83,7 +82,7 @@ class DetectionsConfig(FrigateBaseModel):
description="Seconds to wait after no detection-causing activity before cutting off a detection.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original detections state",
description="Tracks whether detections were originally enabled in the static configuration.",
@@ -129,7 +128,7 @@ class GenAIReviewConfig(FrigateBaseModel):
title="Save thumbnails",
description="Save thumbnails that are sent to the GenAI provider for debugging and review.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None,
title="Original GenAI state",
description="Tracks whether GenAI review was originally enabled in the static configuration.",
+1 -3
View File
@@ -1,5 +1,3 @@
from typing import Optional
from pydantic import Field
from ..base import FrigateBaseModel
@@ -46,7 +44,7 @@ class SnapshotsConfig(FrigateBaseModel):
title="Required zones",
description="Zones an object must enter for a snapshot to be saved.",
)
height: Optional[int] = Field(
height: int | None = Field(
default=None,
title="Snapshot height",
description="Height (pixels) to resize snapshots from API to; leave empty to preserve original size.",
+1 -2
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Optional
from pydantic import Field
@@ -76,7 +75,7 @@ class TimestampStyleConfig(FrigateBaseModel):
title="Timestamp thickness",
description="Line thickness of the timestamp text.",
)
effect: Optional[TimestampEffectEnum] = Field(
effect: TimestampEffectEnum | None = Field(
default=None,
title="Timestamp effect",
description="Visual effect for the timestamp text (none, solid, shadow).",
+9 -10
View File
@@ -1,6 +1,5 @@
# this uses the base model because the color is an extra attribute
import logging
from typing import Optional, Union
import numpy as np
from pydantic import BaseModel, Field, PrivateAttr, field_validator, model_validator
@@ -13,7 +12,7 @@ logger = logging.getLogger(__name__)
class ZoneConfig(BaseModel):
friendly_name: Optional[str] = Field(
friendly_name: str | None = Field(
None,
title="Zone name",
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.",
@@ -23,7 +22,7 @@ class ZoneConfig(BaseModel):
title="Enabled",
description="Enable or disable this zone. Disabled zones are ignored at runtime.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None, title="Keep track of original state of zone."
)
filters: dict[str, FilterConfig] = Field(
@@ -31,11 +30,11 @@ class ZoneConfig(BaseModel):
title="Zone filters",
description="Filters to apply to objects within this zone. Used to reduce false positives or restrict which objects are considered present in the zone.",
)
coordinates: Union[str, list[str]] = Field(
coordinates: str | list[str] = Field(
title="Coordinates",
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).",
)
distances: Optional[Union[str, list[str]]] = Field(
distances: str | list[str] | None = Field(
default_factory=list,
title="Real-world distances",
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.",
@@ -52,18 +51,18 @@ class ZoneConfig(BaseModel):
title="Loitering seconds",
description="Number of seconds an object must remain in the zone to be considered as loitering. Set to 0 to disable loitering detection.",
)
speed_threshold: Optional[float] = Field(
speed_threshold: float | None = Field(
default=None,
ge=0.1,
title="Minimum speed",
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.",
)
objects: Union[str, list[str]] = Field(
objects: str | list[str] = Field(
default_factory=list,
title="Trigger objects",
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.",
)
_color: Optional[tuple[int, int, int]] = PrivateAttr()
_color: tuple[int, int, int] | None = PrivateAttr()
_contour: np.ndarray = PrivateAttr()
@property
@@ -147,7 +146,7 @@ class ZoneConfig(BaseModel):
except ValueError:
raise ValueError(
f"Invalid coordinates found in configuration file. Coordinates must be relative (between 0-1): {coordinates}"
)
) from None
if explicit:
self.coordinates = ",".join(
@@ -176,7 +175,7 @@ class ZoneConfig(BaseModel):
except ValueError:
raise ValueError(
f"Invalid coordinates found in configuration file. Coordinates must be relative (between 0-1): {coordinates}"
)
) from None
if explicit:
self.coordinates = ",".join(
+1 -3
View File
@@ -1,5 +1,3 @@
from typing import Union
from pydantic import Field, field_validator
from .base import FrigateBaseModel
@@ -8,7 +6,7 @@ __all__ = ["CameraGroupConfig"]
class CameraGroupConfig(FrigateBaseModel):
cameras: Union[str, list[str]] = Field(
cameras: str | list[str] = Field(
default_factory=list,
title="Camera list",
description="Array of camera names included in this group.",
+16 -17
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Dict, List, Optional, Union
from pydantic import ConfigDict, Field, field_validator
@@ -68,7 +67,7 @@ class AudioTranscriptionConfig(FrigateBaseModel):
title="Model size",
description="Model size to use for offline audio event transcription.",
)
live_enabled: Optional[bool] = Field(
live_enabled: bool | None = Field(
default=False,
title="Live transcription",
description="Enable streaming live transcription for audio as it is received.",
@@ -98,7 +97,7 @@ class CustomClassificationStateCameraConfig(FrigateBaseModel):
class CustomClassificationStateConfig(FrigateBaseModel):
cameras: Dict[str, CustomClassificationStateCameraConfig] = Field(
cameras: dict[str, CustomClassificationStateCameraConfig] = Field(
title="Classification cameras",
description="Per-camera crop and settings for running state classification.",
)
@@ -160,7 +159,7 @@ class ClassificationConfig(FrigateBaseModel):
title="Bird classification config",
description="Settings specific to bird classification models.",
)
custom: Dict[str, CustomClassificationConfig] = Field(
custom: dict[str, CustomClassificationConfig] = Field(
default={},
title="Custom Classification Models",
description="Configuration for custom classification models used for objects or state detection.",
@@ -173,12 +172,12 @@ class SemanticSearchConfig(FrigateBaseModel):
title="Enable semantic search",
description="Enable or disable the semantic search feature.",
)
reindex: Optional[bool] = Field(
reindex: bool | None = Field(
default=False,
title="Reindex on startup",
description="Trigger a full reindex of historical tracked objects into the embeddings database.",
)
model: Optional[Union[SemanticSearchModelEnum, str]] = Field(
model: SemanticSearchModelEnum | str | None = Field(
default=SemanticSearchModelEnum.jinav1,
title="Semantic search model or GenAI provider name",
description="The embeddings model to use for semantic search (for example 'jinav1'), or the name of a GenAI provider with the embeddings role.",
@@ -199,7 +198,7 @@ class SemanticSearchConfig(FrigateBaseModel):
title="Model size",
description="Select model size; 'small' runs on CPU and 'large' typically requires GPU.",
)
device: Optional[str] = Field(
device: str | None = Field(
default=None,
title="Device",
description="This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information",
@@ -207,7 +206,7 @@ class SemanticSearchConfig(FrigateBaseModel):
class TriggerConfig(FrigateBaseModel):
friendly_name: Optional[str] = Field(
friendly_name: str | None = Field(
None,
title="Friendly name",
description="Optional friendly name displayed in the UI for this trigger.",
@@ -233,7 +232,7 @@ class TriggerConfig(FrigateBaseModel):
gt=0.0,
le=1.0,
)
actions: List[TriggerAction] = Field(
actions: list[TriggerAction] = Field(
default=[],
title="Trigger actions",
description="List of actions to execute when trigger matches (notification, sub_label, attribute).",
@@ -243,7 +242,7 @@ class TriggerConfig(FrigateBaseModel):
class CameraSemanticSearchConfig(FrigateBaseModel):
triggers: Dict[str, TriggerConfig] = Field(
triggers: dict[str, TriggerConfig] = Field(
default={},
title="Triggers",
description="Actions and matching criteria for camera-specific semantic search triggers.",
@@ -307,7 +306,7 @@ class FaceRecognitionConfig(FrigateBaseModel):
title="Blur confidence filter",
description="Adjust confidence scores based on image blur to reduce false positives for poor quality faces.",
)
device: Optional[str] = Field(
device: str | None = Field(
default=None,
title="Device",
description="This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information",
@@ -369,7 +368,7 @@ class LicensePlateRecognitionConfig(FrigateBaseModel):
title="Min plate length",
description="Minimum number of characters a recognized plate must contain to be considered valid.",
)
format: Optional[str] = Field(
format: str | None = Field(
default=None,
title="Plate format regex",
description="Optional regex to validate recognized plate strings against an expected format.",
@@ -380,7 +379,7 @@ class LicensePlateRecognitionConfig(FrigateBaseModel):
description="Number of character mismatches allowed when comparing detected plates to known plates.",
ge=0,
)
known_plates: Optional[Dict[str, List[str]]] = Field(
known_plates: dict[str, list[str]] | None = Field(
default={},
title="Known plates",
description="List of plates or regexes to specially track or alert on.",
@@ -397,12 +396,12 @@ class LicensePlateRecognitionConfig(FrigateBaseModel):
title="Save debug plates",
description="Save plate crop images for debugging LPR performance.",
)
device: Optional[str] = Field(
device: str | None = Field(
default=None,
title="Device",
description="This is an override, to target a specific device. See https://onnxruntime.ai/docs/execution-providers/ for more information",
)
replace_rules: List[ReplaceRule] = Field(
replace_rules: list[ReplaceRule] = Field(
default_factory=list,
title="Replacement rules",
description="Regex replacement rules used to normalize detected plate strings before matching.",
@@ -443,10 +442,10 @@ class CameraAudioTranscriptionConfig(FrigateBaseModel):
title="Enable transcription",
description="Enable or disable manually triggered audio event transcription.",
)
enabled_in_config: Optional[bool] = Field(
enabled_in_config: bool | None = Field(
default=None, title="Original transcription state"
)
live_enabled: Optional[bool] = Field(
live_enabled: bool | None = Field(
default=False,
title="Live transcription",
description="Enable streaming live transcription for audio as it is received.",
+13 -14
View File
@@ -4,7 +4,7 @@ import io
import json
import logging
import os
from typing import Any, Dict, Optional
from typing import Any, Self
import numpy as np
from pydantic import (
@@ -17,7 +17,6 @@ from pydantic import (
model_validator,
)
from ruamel.yaml import YAML
from typing_extensions import Self
from frigate.const import REGEX_JSON
from frigate.detectors import DetectorConfig, ModelConfig
@@ -174,7 +173,7 @@ class RuntimeMotionConfig(MotionConfig):
class RuntimeFilterConfig(FilterConfig):
"""Runtime version of FilterConfig with rasterized masks."""
rasterized_mask: Optional[np.ndarray] = Field(default=None, exclude=True)
rasterized_mask: np.ndarray | None = Field(default=None, exclude=True)
def __init__(self, **config):
frame_shape = config.get("frame_shape", (1, 1))
@@ -293,7 +292,7 @@ def verify_recording_segments_setup_with_reasonable_time(
raise ValueError(
f"Camera {camera_config.name} has no segment_time in \
recording output args, segment args are required for record."
)
) from None
if int(record_args[seg_arg_index + 1]) > 60:
raise ValueError(
@@ -420,7 +419,7 @@ def verify_lpr_and_face(
class FrigateConfig(FrigateBaseModel):
version: Optional[str] = Field(
version: str | None = Field(
default=None,
title="Current config version",
description="Numeric or string version of the active configuration to help detect migrations or format changes.",
@@ -496,7 +495,7 @@ class FrigateConfig(FrigateBaseModel):
)
# Detector config
detectors: Dict[str, BaseDetectorConfig] = Field(
detectors: dict[str, BaseDetectorConfig] = Field(
default=DEFAULT_DETECTORS,
title="Detector hardware",
description="Configuration for object detectors (CPU, GPU, ONNX backends) and any detector-specific model settings.",
@@ -508,14 +507,14 @@ class FrigateConfig(FrigateBaseModel):
)
# GenAI config (named provider configs: name -> GenAIConfig)
genai: Dict[str, GenAIConfig] = Field(
genai: dict[str, GenAIConfig] = Field(
default_factory=dict,
title="Generative AI configuration",
description="Settings for integrated generative AI providers used to generate object descriptions and review summaries.",
)
# Camera config
cameras: Dict[str, CameraConfig] = Field(title="Cameras", description="Cameras")
cameras: dict[str, CameraConfig] = Field(title="Cameras", description="Cameras")
audio: AudioConfig = Field(
default_factory=AudioConfig,
title="Audio detection",
@@ -541,7 +540,7 @@ class FrigateConfig(FrigateBaseModel):
title="Live playback",
description="Settings to control the jsmpeg live stream resolution and quality. This does not affect restreamed cameras that use go2rtc for live view.",
)
motion: Optional[MotionConfig] = Field(
motion: MotionConfig | None = Field(
default=None,
title="Motion detection",
description="Default motion detection settings applied to cameras unless overridden per-camera.",
@@ -599,19 +598,19 @@ class FrigateConfig(FrigateBaseModel):
description="License plate recognition settings including detection thresholds, formatting, and known plates.",
)
camera_groups: Dict[str, CameraGroupConfig] = Field(
camera_groups: dict[str, CameraGroupConfig] = Field(
default_factory=dict,
title="Camera groups",
description="Configuration for named camera groups used to organize cameras in the UI.",
)
profiles: Dict[str, ProfileDefinitionConfig] = Field(
profiles: dict[str, ProfileDefinitionConfig] = Field(
default_factory=dict,
title="Profiles",
description="Named profile definitions with friendly names. Camera profiles must reference names defined here.",
)
active_profile: Optional[str] = Field(
active_profile: str | None = Field(
default=None,
title="Active profile",
description="Currently active profile name. Runtime-only, not persisted in YAML.",
@@ -1054,7 +1053,7 @@ class FrigateConfig(FrigateBaseModel):
@field_validator("cameras")
@classmethod
def ensure_zones_and_cameras_have_different_names(cls, v: Dict[str, CameraConfig]):
def ensure_zones_and_cameras_have_different_names(cls, v: dict[str, CameraConfig]):
zones = [zone for camera in v.values() for zone in camera.zones.keys()]
for zone in zones:
if zone in v.keys():
@@ -1136,7 +1135,7 @@ class FrigateConfig(FrigateBaseModel):
@classmethod
def parse_object(
cls, obj: Any, *, plus_api: Optional[PlusApi] = None, install: bool = False
cls, obj: Any, *, plus_api: PlusApi | None = None, install: bool = False
):
return cls.model_validate(
obj, context={"plus_api": plus_api, "install": install}
+2 -1
View File
@@ -1,5 +1,6 @@
from typing import Self
from pydantic import Field, ValidationInfo, model_validator
from typing_extensions import Self
from frigate.log import LogLevel, apply_log_levels
+7 -8
View File
@@ -1,7 +1,6 @@
from typing import Optional
from typing import Self
from pydantic import Field, ValidationInfo, model_validator
from typing_extensions import Self
from frigate.const import FREQUENCY_STATS_POINTS
@@ -43,33 +42,33 @@ class MqttConfig(FrigateBaseModel):
title="Stats interval",
description="Interval in seconds for publishing system and camera stats to MQTT.",
)
user: Optional[EnvString] = Field(
user: EnvString | None = Field(
default=None,
title="MQTT username",
description="Optional MQTT username; can be provided via environment variables or secrets.",
)
password: Optional[EnvString] = Field(
password: EnvString | None = Field(
default=None,
title="MQTT password",
description="Optional MQTT password; can be provided via environment variables or secrets.",
validate_default=True,
)
tls_ca_certs: Optional[str] = Field(
tls_ca_certs: str | None = Field(
default=None,
title="TLS CA certs",
description="Path to CA certificate for TLS connections to the broker (for self-signed certs).",
)
tls_client_cert: Optional[str] = Field(
tls_client_cert: str | None = Field(
default=None,
title="Client cert",
description="Client certificate path for TLS mutual authentication; do not set user/password when using client certs.",
)
tls_client_key: Optional[str] = Field(
tls_client_key: str | None = Field(
default=None,
title="Client key",
description="Private key path for the client certificate.",
)
tls_insecure: Optional[bool] = Field(
tls_insecure: bool | None = Field(
default=None,
title="TLS insecure",
description="Allow insecure TLS connections by skipping hostname verification (not recommended).",
+2 -4
View File
@@ -1,5 +1,3 @@
from typing import Union
from pydantic import Field
from .base import FrigateBaseModel
@@ -16,12 +14,12 @@ class IPv6Config(FrigateBaseModel):
class ListenConfig(FrigateBaseModel):
internal: Union[int, str] = Field(
internal: int | str = Field(
default=5000,
title="Internal port",
description="Internal listening port for Frigate (default 5000).",
)
external: Union[int, str] = Field(
external: int | str = Field(
default=8971,
title="External port",
description="External listening port for Frigate (default 8971).",
+9 -8
View File
@@ -3,9 +3,10 @@
import copy
import json
import logging
from datetime import datetime, timezone
from collections.abc import Callable
from datetime import UTC, datetime
from pathlib import Path
from typing import Any, Callable, Optional
from typing import Any
from frigate.config.camera.updater import (
CameraConfigUpdateEnum,
@@ -169,9 +170,9 @@ class ProfileManager:
def activate_profile(
self,
profile_name: Optional[str],
profile_name: str | None,
clear_runtime_overrides: bool = True,
) -> Optional[str]:
) -> str | None:
"""Activate a profile by name, or deactivate if None.
Args:
@@ -256,7 +257,7 @@ class ProfileManager:
def _apply_profile_overrides(
self, profile_name: str, changed: dict[str, set[str]]
) -> Optional[str]:
) -> str | None:
"""Apply profile overrides for all cameras that have the named profile."""
for cam_name, cam_config in self.config.cameras.items():
profile = cam_config.profiles.get(profile_name)
@@ -358,14 +359,14 @@ class ProfileManager:
retain=True,
)
def _persist_active_profile(self, profile_name: Optional[str]) -> None:
def _persist_active_profile(self, profile_name: str | None) -> None:
"""Persist the active profile state to disk as JSON."""
try:
data = self._load_persisted_data()
data["active"] = profile_name
if profile_name is not None:
data.setdefault("last_activated", {})[profile_name] = datetime.now(
timezone.utc
UTC
).timestamp()
PERSISTENCE_FILE.write_text(json.dumps(data))
except OSError:
@@ -384,7 +385,7 @@ class ProfileManager:
return {"active": None, "last_activated": {}}
@staticmethod
def load_persisted_profile() -> Optional[str]:
def load_persisted_profile() -> str | None:
"""Load the persisted active profile name from disk."""
data = ProfileManager._load_persisted_data()
name = data.get("active")
+5 -7
View File
@@ -1,5 +1,3 @@
from typing import Optional
from pydantic import Field, field_validator
from .base import FrigateBaseModel
@@ -19,7 +17,7 @@ class HeaderMappingConfig(FrigateBaseModel):
title="Role header",
description="Header containing the authenticated user's role or groups from the upstream proxy.",
)
role_map: Optional[dict[str, list[str]]] = Field(
role_map: dict[str, list[str]] | None = Field(
default_factory=dict,
title=("Role mapping"),
description="Map upstream group values to Frigate roles (for example map admin groups to the admin role).",
@@ -32,22 +30,22 @@ class ProxyConfig(FrigateBaseModel):
title="Header mapping",
description="Map incoming proxy headers to Frigate user and role fields for proxy-based auth.",
)
logout_url: Optional[str] = Field(
logout_url: str | None = Field(
default=None,
title="Logout URL",
description="URL to redirect users to when logging out via the proxy.",
)
auth_secret: Optional[EnvString] = Field(
auth_secret: EnvString | None = Field(
default=None,
title="Proxy secret",
description="Optional secret checked against the X-Proxy-Secret header to verify trusted proxies.",
)
default_role: Optional[str] = Field(
default_role: str | None = Field(
default="viewer",
title="Default role",
description="Default role assigned to proxy-authenticated users when no role mapping applies.",
)
separator: Optional[str] = Field(
separator: str | None = Field(
default=",",
title="Separator character",
description="Character used to split multiple values provided in proxy headers.",
+1 -3
View File
@@ -1,5 +1,3 @@
from typing import Optional
from pydantic import Field
from .base import FrigateBaseModel
@@ -23,7 +21,7 @@ class StatsConfig(FrigateBaseModel):
title="Network bandwidth",
description="Enable per-process network bandwidth monitoring for camera ffmpeg processes and detectors (requires capabilities).",
)
intel_gpu_device: Optional[str] = Field(
intel_gpu_device: str | None = Field(
default=None,
title="Intel GPU device",
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.",
+1 -2
View File
@@ -1,5 +1,4 @@
from enum import Enum
from typing import Optional
from pydantic import Field
@@ -20,7 +19,7 @@ class UnitSystemEnum(str, Enum):
class UIConfig(FrigateBaseModel):
timezone: Optional[str] = Field(
timezone: str | None = Field(
default=None,
title="Timezone",
description="Optional timezone to display across the UI (defaults to browser local time if unset).",