2021-05-22 18:28:15 -04:00
from __future__ import annotations
2020-11-03 08:15:58 -06:00
import json
2020-12-21 08:03:27 -06:00
import logging
2020-11-03 08:15:58 -06:00
import os
2021-08-16 08:02:04 -05:00
from enum import Enum
2023-11-02 10:35:30 +00:00
from pathlib import Path
2024-02-29 16:10:13 -07:00
from typing import Any , Dict , List , Optional , Tuple , Union
2020-11-03 08:15:58 -06:00
import numpy as np
2024-02-29 16:10:13 -07:00
from pydantic import (
BaseModel ,
ConfigDict ,
Field ,
TypeAdapter ,
ValidationInfo ,
field_serializer ,
field_validator ,
)
2021-06-24 01:45:27 -04:00
from pydantic.fields import PrivateAttr
2020-11-01 06:17:44 -06:00
2023-07-17 05:07:15 -06:00
from frigate.const import (
2023-09-30 06:38:15 -06:00
ALL_ATTRIBUTE_LABELS ,
2023-07-17 05:07:15 -06:00
AUDIO_MIN_CONFIDENCE ,
CACHE_DIR ,
2023-11-05 13:30:29 -07:00
CACHE_SEGMENT_FORMAT ,
2023-07-17 05:07:15 -06:00
DEFAULT_DB_PATH ,
2024-07-01 16:08:14 -06:00
FREQUENCY_STATS_POINTS ,
2023-11-18 14:37:06 -07:00
MAX_PRE_CAPTURE ,
2023-07-17 05:07:15 -06:00
REGEX_CAMERA_NAME ,
YAML_EXT ,
)
2023-05-29 12:31:17 +02:00
from frigate.detectors import DetectorConfig , ModelConfig
2023-04-24 07:24:28 -05:00
from frigate.detectors.detector_config import BaseDetectorConfig
2022-11-28 20:48:11 -07:00
from frigate.ffmpeg_presets import (
2022-12-30 09:56:52 -07:00
parse_preset_hardware_acceleration_decode ,
parse_preset_hardware_acceleration_scale ,
2022-11-28 20:48:11 -07:00
parse_preset_input ,
parse_preset_output_record ,
)
2023-05-29 12:31:17 +02:00
from frigate.plus import PlusApi
2023-07-06 08:28:50 -06:00
from frigate.util.builtin import (
2023-05-29 12:31:17 +02:00
deep_merge ,
escape_special_characters ,
2024-07-07 13:53:00 -05:00
generate_color_palette ,
2023-05-29 12:31:17 +02:00
get_ffmpeg_arg_list ,
load_config_with_no_duplicates ,
2022-12-15 07:12:52 -06:00
)
2024-05-29 07:41:41 -06:00
from frigate.util.config import StreamInfoRetriever , get_relative_coordinates
2023-07-06 08:28:50 -06:00
from frigate.util.image import create_mask
2024-05-29 07:41:41 -06:00
from frigate.util.services import auto_detect_hwaccel
2022-12-15 07:12:52 -06:00
2020-12-21 08:03:27 -06:00
logger = logging . getLogger ( __name__ )
2021-06-15 22:19:49 +02:00
# TODO: Identify what the default format to display timestamps is
DEFAULT_TIME_FORMAT = "%m/ %d /%Y %H:%M:%S"
# German Style:
# DEFAULT_TIME_FORMAT = "%d.%m.%Y %H:%M:%S"
2021-01-16 07:39:08 -06:00
2021-06-24 01:45:27 -04:00
FRIGATE_ENV_VARS = { k : v for k , v in os . environ . items () if k . startswith ( "FRIGATE_" )}
2023-11-02 10:35:30 +00:00
# read docker secret files as env vars too
2024-02-28 23:18:34 +00:00
if os . path . isdir ( "/run/secrets" ) and os . access ( "/run/secrets" , os . R_OK ):
2023-11-02 10:35:30 +00:00
for secret_file in os . listdir ( "/run/secrets" ):
if secret_file . startswith ( "FRIGATE_" ):
2024-07-12 21:36:15 +08:00
FRIGATE_ENV_VARS [ secret_file ] = (
Path ( os . path . join ( "/run/secrets" , secret_file )) . read_text () . strip ()
)
2021-06-24 01:45:27 -04:00
2021-06-15 22:19:49 +02:00
DEFAULT_TRACKED_OBJECTS = [ "person" ]
2024-02-20 16:26:09 -07:00
DEFAULT_ALERT_OBJECTS = [ "person" , "car" ]
2023-09-21 04:23:26 -06:00
DEFAULT_LISTEN_AUDIO = [ "bark" , "fire_alarm" , "scream" , "speech" , "yell" ]
2021-08-16 07:39:20 -05:00
DEFAULT_DETECTORS = { "cpu" : { "type" : "cpu" }}
2023-07-14 14:56:03 +03:00
DEFAULT_DETECT_DIMENSIONS = { "width" : 1280 , "height" : 720 }
2023-09-21 06:20:05 -06:00
DEFAULT_TIME_LAPSE_FFMPEG_ARGS = "-vf setpts=0.04*PTS -r 30"
2020-11-01 06:17:44 -06:00
2024-05-29 07:41:41 -06:00
# stream info handler
stream_info_retriever = StreamInfoRetriever ()
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class FrigateBaseModel ( BaseModel ):
2024-02-29 16:10:13 -07:00
model_config = ConfigDict ( extra = "forbid" , protected_namespaces = ())
2021-09-04 16:56:01 -05:00
2023-01-13 16:27:16 -07:00
class LiveModeEnum ( str , Enum ):
jsmpeg = "jsmpeg"
mse = "mse"
webrtc = "webrtc"
2023-02-26 08:37:18 -07:00
class TimeFormatEnum ( str , Enum ):
browser = "browser"
hours12 = "12hour"
hours24 = "24hour"
2023-02-22 14:54:16 +01:00
class DateTimeStyleEnum ( str , Enum ):
full = "full"
long = "long"
medium = "medium"
short = "short"
2022-02-27 08:04:12 -06:00
class UIConfig ( FrigateBaseModel ):
2024-02-29 16:10:13 -07:00
timezone : Optional [ str ] = Field ( default = None , title = "Override UI timezone." )
2023-02-26 08:37:18 -07:00
time_format : TimeFormatEnum = Field (
default = TimeFormatEnum . browser , title = "Override UI time format."
)
2023-02-22 14:54:16 +01:00
date_style : DateTimeStyleEnum = Field (
default = DateTimeStyleEnum . short , title = "Override UI dateStyle."
)
time_style : DateTimeStyleEnum = Field (
default = DateTimeStyleEnum . medium , title = "Override UI timeStyle."
)
strftime_fmt : Optional [ str ] = Field (
default = None , title = "Override date and time format using strftime syntax."
)
2021-05-22 18:28:15 -04:00
2022-04-16 08:42:44 -05:00
2024-06-02 07:48:28 -05:00
class TlsConfig ( FrigateBaseModel ):
2024-06-29 07:18:40 -06:00
enabled : bool = Field ( default = True , title = "Enable TLS for port 8971" )
2024-06-02 07:48:28 -05:00
2024-05-18 11:36:13 -05:00
class HeaderMappingConfig ( FrigateBaseModel ):
user : str = Field (
default = None , title = "Header name from upstream proxy to identify user."
)
2024-06-14 18:02:13 -05:00
class ProxyConfig ( FrigateBaseModel ):
header_map : HeaderMappingConfig = Field (
default_factory = HeaderMappingConfig ,
title = "Header mapping definitions for proxy user passing." ,
)
logout_url : Optional [ str ] = Field (
default = None , title = "Redirect url for logging out with proxy."
)
auth_secret : Optional [ str ] = Field (
default = None ,
title = "Secret value for proxy authentication." ,
)
2024-05-18 11:36:13 -05:00
class AuthConfig ( FrigateBaseModel ):
2024-06-14 18:02:13 -05:00
enabled : bool = Field ( default = True , title = "Enable authentication" )
2024-05-18 11:36:13 -05:00
reset_admin_password : bool = Field (
default = False , title = "Reset the admin password on startup"
)
cookie_name : str = Field (
default = "frigate_token" , title = "Name for jwt token cookie" , pattern = r "^[a-z]_*$"
)
2024-05-18 14:53:49 -05:00
cookie_secure : bool = Field ( default = False , title = "Set secure flag on cookie" )
2024-05-18 11:36:13 -05:00
session_length : int = Field (
default = 86400 , title = "Session length for jwt session tokens" , ge = 60
)
refresh_time : int = Field (
default = 43200 ,
title = "Refresh the session if it is going to expire in this many seconds" ,
ge = 30 ,
)
failed_login_rate_limit : Optional [ str ] = Field (
default = None ,
title = "Rate limits for failed login attempts." ,
)
trusted_proxies : List [ str ] = Field (
default = [],
title = "Trusted proxies for determining IP address to rate limit" ,
)
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
hash_iterations : int = Field ( default = 600000 , title = "Password hash iterations" )
2023-06-11 07:26:34 -06:00
class StatsConfig ( FrigateBaseModel ):
amd_gpu_stats : bool = Field ( default = True , title = "Enable AMD GPU stats." )
intel_gpu_stats : bool = Field ( default = True , title = "Enable Intel GPU stats." )
network_bandwidth : bool = Field (
default = False , title = "Enable network bandwidth for ffmpeg processes."
)
2023-01-26 00:36:26 +00:00
class TelemetryConfig ( FrigateBaseModel ):
2023-06-11 15:34:03 +03:00
network_interfaces : List [ str ] = Field (
2023-09-21 04:26:22 -06:00
default = [],
2023-06-11 15:34:03 +03:00
title = "Enabled network interfaces for bandwidth calculation." ,
)
2023-06-11 07:26:34 -06:00
stats : StatsConfig = Field (
default_factory = StatsConfig , title = "System Stats Configuration"
)
2023-01-26 00:36:26 +00:00
version_check : bool = Field ( default = True , title = "Enable latest version check." )
2021-09-04 16:56:01 -05:00
class MqttConfig ( FrigateBaseModel ):
2022-11-23 19:03:20 -07:00
enabled : bool = Field ( title = "Enable MQTT Communication." , default = True )
2023-01-03 18:24:53 -07:00
host : str = Field ( default = "" , title = "MQTT Host" )
2021-06-24 01:45:27 -04:00
port : int = Field ( default = 1883 , title = "MQTT Port" )
topic_prefix : str = Field ( default = "frigate" , title = "MQTT Topic Prefix" )
client_id : str = Field ( default = "frigate" , title = "MQTT Client ID" )
2024-07-01 16:08:14 -06:00
stats_interval : int = Field (
default = 60 , ge = FREQUENCY_STATS_POINTS , title = "MQTT Camera Stats Interval"
)
2024-02-29 16:10:13 -07:00
user : Optional [ str ] = Field ( None , title = "MQTT Username" )
password : Optional [ str ] = Field ( None , title = "MQTT Password" , validate_default = True )
tls_ca_certs : Optional [ str ] = Field ( None , title = "MQTT TLS CA Certificates" )
tls_client_cert : Optional [ str ] = Field ( None , title = "MQTT TLS Client Certificate" )
tls_client_key : Optional [ str ] = Field ( None , title = "MQTT TLS Client Key" )
tls_insecure : Optional [ bool ] = Field ( None , title = "MQTT TLS Insecure" )
2021-05-22 18:28:15 -04:00
2024-02-29 16:10:13 -07:00
@field_validator ( "password" )
def user_requires_pass ( cls , v , info : ValidationInfo ):
if ( v is None ) != ( info . data [ "user" ] is None ):
2021-06-24 01:45:27 -04:00
raise ValueError ( "Password must be provided with username." )
2021-06-24 16:53:01 -04:00
return v
2021-05-22 18:28:15 -04:00
2023-09-27 06:19:10 -05:00
class ZoomingModeEnum ( str , Enum ):
disabled = "disabled"
absolute = "absolute"
relative = "relative"
2023-07-08 07:04:47 -05:00
class PtzAutotrackConfig ( FrigateBaseModel ):
enabled : bool = Field ( default = False , title = "Enable PTZ object autotracking." )
2023-09-27 06:19:10 -05:00
calibrate_on_startup : bool = Field (
default = False , title = "Perform a camera calibration when Frigate starts."
)
zooming : ZoomingModeEnum = Field (
default = ZoomingModeEnum . disabled , title = "Autotracker zooming mode."
)
2023-09-28 18:21:37 -05:00
zoom_factor : float = Field (
default = 0.3 ,
title = "Zooming factor (0.1-0.75)." ,
ge = 0.1 ,
le = 0.75 ,
)
2023-07-08 07:04:47 -05:00
track : List [ str ] = Field ( default = DEFAULT_TRACKED_OBJECTS , title = "Objects to track." )
required_zones : List [ str ] = Field (
default_factory = list ,
title = "List of required zones to be entered in order to begin autotracking." ,
)
return_preset : str = Field (
default = "home" ,
title = "Name of camera preset to return to when object tracking is over." ,
)
timeout : int = Field (
default = 10 , title = "Seconds to delay before returning to preset."
)
2023-11-01 06:12:43 -05:00
movement_weights : Optional [ Union [ str , List [ str ]]] = Field (
2023-09-27 06:19:10 -05:00
default = [],
title = "Internal value used for PTZ movements based on the speed of your camera's motor." ,
)
2023-11-15 19:25:48 -06:00
enabled_in_config : Optional [ bool ] = Field (
2024-02-29 16:10:13 -07:00
None , title = "Keep track of original state of autotracking."
2023-11-15 19:25:48 -06:00
)
2023-09-27 06:19:10 -05:00
2024-02-29 16:10:13 -07:00
@field_validator ( "movement_weights" , mode = "before" )
@classmethod
2023-09-27 06:19:10 -05:00
def validate_weights ( cls , v ):
if v is None :
return None
if isinstance ( v , str ):
2024-02-29 16:10:13 -07:00
weights = list ( map ( str , map ( float , v . split ( "," ))))
2023-09-27 06:19:10 -05:00
elif isinstance ( v , list ):
2024-02-29 16:10:13 -07:00
weights = [ str ( float ( val )) for val in v ]
2023-09-27 06:19:10 -05:00
else :
raise ValueError ( "Invalid type for movement_weights" )
2023-10-22 11:59:13 -05:00
if len ( weights ) != 5 :
raise ValueError ( "movement_weights must have exactly 5 floats" )
2023-09-27 06:19:10 -05:00
return weights
2023-07-08 07:04:47 -05:00
2023-04-26 05:08:53 -06:00
class OnvifConfig ( FrigateBaseModel ):
host : str = Field ( default = "" , title = "Onvif Host" )
port : int = Field ( default = 8000 , title = "Onvif Port" )
2024-02-29 16:10:13 -07:00
user : Optional [ str ] = Field ( None , title = "Onvif Username" )
password : Optional [ str ] = Field ( None , title = "Onvif Password" )
2023-07-08 07:04:47 -05:00
autotracking : PtzAutotrackConfig = Field (
default_factory = PtzAutotrackConfig ,
title = "PTZ auto tracking config." ,
)
2024-07-09 14:00:47 -05:00
ignore_time_mismatch : bool = Field (
default = False ,
title = "Onvif Ignore Time Synchronization Mismatch Between Camera and Server" ,
)
2023-04-26 05:08:53 -06:00
2021-12-11 09:22:44 -06:00
class RetainModeEnum ( str , Enum ):
all = "all"
motion = "motion"
active_objects = "active_objects"
2021-09-04 16:56:01 -05:00
class RetainConfig ( FrigateBaseModel ):
2021-09-20 18:59:16 -05:00
default : float = Field ( default = 10 , title = "Default retention period." )
2022-02-05 09:28:21 -06:00
mode : RetainModeEnum = Field ( default = RetainModeEnum . motion , title = "Retain mode." )
2021-09-20 18:59:16 -05:00
objects : Dict [ str , float ] = Field (
2021-06-24 01:45:27 -04:00
default_factory = dict , title = "Object retention period."
)
2020-11-23 08:25:46 -06:00
2021-05-22 18:28:15 -04:00
2021-09-08 08:02:26 -05:00
class EventsConfig ( FrigateBaseModel ):
2023-11-18 14:37:06 -07:00
pre_capture : int = Field (
default = 5 , title = "Seconds to retain before event starts." , le = MAX_PRE_CAPTURE
)
2021-09-08 08:02:26 -05:00
post_capture : int = Field ( default = 5 , title = "Seconds to retain after event ends." )
2021-07-09 16:14:16 -04:00
objects : Optional [ List [ str ]] = Field (
2024-02-29 16:10:13 -07:00
None ,
2021-09-08 08:02:26 -05:00
title = "List of objects to be detected in order to save the event." ,
2021-07-09 16:14:16 -04:00
)
2021-06-24 01:45:27 -04:00
retain : RetainConfig = Field (
2021-09-08 08:02:26 -05:00
default_factory = RetainConfig , title = "Event retention settings."
2021-06-24 01:45:27 -04:00
)
2021-05-22 18:28:15 -04:00
2021-12-11 09:22:44 -06:00
class RecordRetainConfig ( FrigateBaseModel ):
days : float = Field ( default = 0 , title = "Default retention period." )
mode : RetainModeEnum = Field ( default = RetainModeEnum . all , title = "Retain mode." )
2023-09-21 06:20:05 -06:00
class RecordExportConfig ( FrigateBaseModel ):
timelapse_args : str = Field (
default = DEFAULT_TIME_LAPSE_FFMPEG_ARGS , title = "Timelapse Args"
)
2023-12-03 07:16:01 -07:00
class RecordQualityEnum ( str , Enum ):
very_low = "very_low"
low = "low"
medium = "medium"
high = "high"
very_high = "very_high"
class RecordPreviewConfig ( FrigateBaseModel ):
quality : RecordQualityEnum = Field (
default = RecordQualityEnum . medium , title = "Quality of recording preview."
)
2021-09-04 16:56:01 -05:00
class RecordConfig ( FrigateBaseModel ):
2021-07-09 16:14:16 -04:00
enabled : bool = Field ( default = False , title = "Enable record on all cameras." )
2023-11-18 05:06:00 -07:00
sync_recordings : bool = Field (
default = False , title = "Sync recordings with disk on startup and once a day."
2023-07-15 07:38:21 -06:00
)
2022-02-05 08:51:18 -06:00
expire_interval : int = Field (
default = 60 ,
title = "Number of minutes to wait between cleanup runs." ,
)
2021-12-11 09:22:44 -06:00
retain : RecordRetainConfig = Field (
default_factory = RecordRetainConfig , title = "Record retention settings."
)
2021-09-08 08:02:26 -05:00
events : EventsConfig = Field (
default_factory = EventsConfig , title = "Event specific settings."
2021-07-09 16:14:16 -04:00
)
2023-09-21 06:20:05 -06:00
export : RecordExportConfig = Field (
default_factory = RecordExportConfig , title = "Recording Export Config"
)
2023-12-03 07:16:01 -07:00
preview : RecordPreviewConfig = Field (
default_factory = RecordPreviewConfig , title = "Recording Preview Config"
)
2023-05-15 06:36:26 -06:00
enabled_in_config : Optional [ bool ] = Field (
2024-02-29 16:10:13 -07:00
None , title = "Keep track of original state of recording."
2023-05-15 06:36:26 -06:00
)
2021-07-09 16:14:16 -04:00
2021-09-04 16:56:01 -05:00
class MotionConfig ( FrigateBaseModel ):
2024-02-19 06:26:59 -07:00
enabled : bool = Field ( default = True , title = "Enable motion on all cameras." )
2021-06-24 01:45:27 -04:00
threshold : int = Field (
2023-06-30 07:27:31 -05:00
default = 30 ,
2021-06-24 01:45:27 -04:00
title = "Motion detection threshold (1-255)." ,
ge = 1 ,
le = 255 ,
)
2023-06-11 09:45:11 -04:00
lightning_threshold : float = Field (
default = 0.8 , title = "Lightning detection threshold (0.3-1.0)." , ge = 0.3 , le = 1.0
)
improve_contrast : bool = Field ( default = True , title = "Improve Contrast" )
2023-06-18 11:56:41 -05:00
contour_area : Optional [ int ] = Field ( default = 10 , title = "Contour Area" )
2021-06-24 01:45:27 -04:00
delta_alpha : float = Field ( default = 0.2 , title = "Delta Alpha" )
2023-06-21 08:38:51 -05:00
frame_alpha : float = Field ( default = 0.01 , title = "Frame Alpha" )
2023-06-18 11:56:41 -05:00
frame_height : Optional [ int ] = Field ( default = 100 , title = "Frame Height" )
2021-06-24 01:45:27 -04:00
mask : Union [ str , List [ str ]] = Field (
default = "" , title = "Coordinates polygon for the motion mask."
)
2022-05-15 06:03:33 -06:00
mqtt_off_delay : int = Field (
default = 30 ,
title = "Delay for updating MQTT with no motion detected." ,
)
2024-02-19 06:26:59 -07:00
enabled_in_config : Optional [ bool ] = Field (
2024-02-29 16:10:13 -07:00
None , title = "Keep track of original state of motion detection."
2024-02-19 06:26:59 -07:00
)
2024-02-29 16:10:13 -07:00
raw_mask : Union [ str , List [ str ]] = ""
@field_serializer ( "mask" , when_used = "json" )
def serialize_mask ( self , value : Any , info ):
return self . raw_mask
@field_serializer ( "raw_mask" , when_used = "json" )
def serialize_raw_mask ( self , value : Any , info ):
return None
2020-11-01 06:17:44 -06:00
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
class RuntimeMotionConfig ( MotionConfig ):
raw_mask : Union [ str , List [ str ]] = ""
mask : np . ndarray = None
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
frame_shape = config . get ( "frame_shape" , ( 1 , 1 ))
2021-05-22 18:28:15 -04:00
2024-04-18 10:35:16 -06:00
mask = get_relative_coordinates ( config . get ( "mask" , "" ), frame_shape )
2021-06-24 01:45:27 -04:00
config [ "raw_mask" ] = mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
if mask :
config [ "mask" ] = create_mask ( frame_shape , mask )
2021-05-22 18:28:15 -04:00
else :
2021-06-24 01:45:27 -04:00
empty_mask = np . zeros ( frame_shape , np . uint8 )
empty_mask [:] = 255
config [ "mask" ] = empty_mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
super () . __init__ ( ** config )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def dict ( self , ** kwargs ):
2024-02-29 16:10:13 -07:00
ret = super () . model_dump ( ** kwargs )
2021-06-24 01:45:27 -04:00
if "mask" in ret :
ret [ "mask" ] = ret [ "raw_mask" ]
ret . pop ( "raw_mask" )
return ret
2024-02-29 16:10:13 -07:00
@field_serializer ( "mask" , when_used = "json" )
def serialize_mask ( self , value : Any , info ):
return self . raw_mask
@field_serializer ( "raw_mask" , when_used = "json" )
def serialize_raw_mask ( self , value : Any , info ):
return None
model_config = ConfigDict ( arbitrary_types_allowed = True , extra = "ignore" )
2021-05-22 18:28:15 -04:00
2022-02-13 08:58:44 -06:00
class StationaryMaxFramesConfig ( FrigateBaseModel ):
2024-02-29 16:10:13 -07:00
default : Optional [ int ] = Field ( None , title = "Default max frames." , ge = 1 )
2022-02-13 08:58:44 -06:00
objects : Dict [ str , int ] = Field (
default_factory = dict , title = "Object specific max frames."
)
class StationaryConfig ( FrigateBaseModel ):
interval : Optional [ int ] = Field (
2024-02-29 16:10:13 -07:00
None ,
2022-02-13 08:58:44 -06:00
title = "Frame interval for checking stationary objects." ,
2023-06-16 07:32:43 -05:00
gt = 0 ,
2022-02-13 08:58:44 -06:00
)
threshold : Optional [ int ] = Field (
2024-02-29 16:10:13 -07:00
None ,
2022-02-13 08:58:44 -06:00
title = "Number of frames without a position change for an object to be considered stationary" ,
ge = 1 ,
)
max_frames : StationaryMaxFramesConfig = Field (
default_factory = StationaryMaxFramesConfig ,
title = "Max frames for stationary objects." ,
)
2021-09-04 16:56:01 -05:00
class DetectConfig ( FrigateBaseModel ):
2024-02-29 16:10:13 -07:00
height : Optional [ int ] = Field (
None , title = "Height of the stream for the detect role."
)
width : Optional [ int ] = Field ( None , title = "Width of the stream for the detect role." )
2021-08-28 08:51:29 -05:00
fps : int = Field (
default = 5 , title = "Number of frames per second to process through detection."
)
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = True , title = "Detection Enabled." )
2023-10-30 18:26:31 -06:00
min_initialized : Optional [ int ] = Field (
2024-02-29 16:10:13 -07:00
None ,
title = "Minimum number of consecutive hits for an object to be initialized by the tracker." ,
2023-10-30 18:26:31 -06:00
)
2021-06-26 09:57:32 -05:00
max_disappeared : Optional [ int ] = Field (
2024-02-29 16:10:13 -07:00
None ,
2024-03-30 18:51:41 +08:00
title = "Maximum number of frames the object can disappear before detection ends." ,
2021-06-24 01:45:27 -04:00
)
2022-02-13 08:58:44 -06:00
stationary : StationaryConfig = Field (
default_factory = StationaryConfig ,
title = "Stationary objects config." ,
2022-02-08 07:40:45 -06:00
)
2023-04-28 05:02:06 -06:00
annotation_offset : int = Field (
default = 0 , title = "Milliseconds to offset detect annotations by."
)
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class FilterConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
min_area : int = Field (
default = 0 , title = "Minimum area of bounding box for object to be counted."
)
max_area : int = Field (
default = 24000000 , title = "Maximum area of bounding box for object to be counted."
)
2022-04-10 09:25:18 -04:00
min_ratio : float = Field (
default = 0 ,
title = "Minimum ratio of bounding box's width/height for object to be counted." ,
)
max_ratio : float = Field (
default = 24000000 ,
title = "Maximum ratio of bounding box's width/height for object to be counted." ,
)
2021-06-24 01:45:27 -04:00
threshold : float = Field (
default = 0.7 ,
title = "Average detection confidence threshold for object to be counted." ,
)
min_score : float = Field (
default = 0.5 , title = "Minimum detection confidence for object to be counted."
)
mask : Optional [ Union [ str , List [ str ]]] = Field (
2024-02-29 16:10:13 -07:00
None ,
2021-06-24 01:45:27 -04:00
title = "Detection area polygon mask for this filter configuration." ,
)
2024-02-29 16:10:13 -07:00
raw_mask : Union [ str , List [ str ]] = ""
@field_serializer ( "mask" , when_used = "json" )
def serialize_mask ( self , value : Any , info ):
return self . raw_mask
@field_serializer ( "raw_mask" , when_used = "json" )
def serialize_raw_mask ( self , value : Any , info ):
return None
2021-05-22 18:28:15 -04:00
2023-07-17 05:07:15 -06:00
class AudioFilterConfig ( FrigateBaseModel ):
threshold : float = Field (
default = 0.8 ,
ge = AUDIO_MIN_CONFIDENCE ,
lt = 1.0 ,
title = "Minimum detection confidence threshold for audio to be counted." ,
)
2021-06-24 01:45:27 -04:00
class RuntimeFilterConfig ( FilterConfig ):
2024-02-29 16:10:13 -07:00
mask : Optional [ np . ndarray ] = None
raw_mask : Optional [ Union [ str , List [ str ]]] = None
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
2024-04-09 16:51:38 -06:00
frame_shape = config . get ( "frame_shape" , ( 1 , 1 ))
2024-04-18 10:35:16 -06:00
mask = get_relative_coordinates ( config . get ( "mask" ), frame_shape )
2024-04-09 16:51:38 -06:00
2021-06-24 01:45:27 -04:00
config [ "raw_mask" ] = mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
if mask is not None :
2024-04-09 16:51:38 -06:00
config [ "mask" ] = create_mask ( frame_shape , mask )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
super () . __init__ ( ** config )
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
def dict ( self , ** kwargs ):
2024-02-29 16:10:13 -07:00
ret = super () . model_dump ( ** kwargs )
2021-06-24 01:45:27 -04:00
if "mask" in ret :
ret [ "mask" ] = ret [ "raw_mask" ]
ret . pop ( "raw_mask" )
return ret
2021-05-22 18:28:15 -04:00
2024-02-29 16:10:13 -07:00
model_config = ConfigDict ( arbitrary_types_allowed = True , extra = "ignore" )
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
# this uses the base model because the color is an extra attribute
2021-06-24 01:45:27 -04:00
class ZoneConfig ( BaseModel ):
filters : Dict [ str , FilterConfig ] = Field (
default_factory = dict , title = "Zone filters."
)
coordinates : Union [ str , List [ str ]] = Field (
title = "Coordinates polygon for the defined zone."
)
2023-06-11 07:00:53 -06:00
inertia : int = Field (
default = 3 ,
title = "Number of consecutive frames required for object to be considered present in the zone." ,
gt = 0 ,
)
2024-03-19 14:58:04 -06:00
loitering_time : int = Field (
default = 0 ,
ge = 0 ,
title = "Number of seconds that an object must loiter to be considered in the zone." ,
)
2024-05-01 09:08:10 -05:00
objects : Union [ str , List [ str ]] = Field (
2021-07-07 07:31:42 -05:00
default_factory = list ,
title = "List of objects that can trigger the zone." ,
)
2021-06-24 01:45:27 -04:00
_color : Optional [ Tuple [ int , int , int ]] = PrivateAttr ()
_contour : np . ndarray = PrivateAttr ()
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
@property
def color ( self ) -> Tuple [ int , int , int ]:
return self . _color
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
@property
def contour ( self ) -> np . ndarray :
return self . _contour
2021-05-22 18:28:15 -04:00
2024-04-19 06:34:07 -05:00
@field_validator ( "objects" , mode = "before" )
@classmethod
def validate_objects ( cls , v ):
if isinstance ( v , str ) and "," not in v :
return [ v ]
return v
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
super () . __init__ ( ** config )
self . _color = config . get ( "color" , ( 0 , 0 , 0 ))
2024-04-09 16:51:38 -06:00
self . _contour = config . get ( "contour" , np . array ([]))
2021-05-22 18:28:15 -04:00
2024-04-09 16:51:38 -06:00
def generate_contour ( self , frame_shape : tuple [ int , int ]):
coordinates = self . coordinates
# masks and zones are saved as relative coordinates
# we know if any points are > 1 then it is using the
# old native resolution coordinates
2021-05-22 18:28:15 -04:00
if isinstance ( coordinates , list ):
2024-04-09 16:51:38 -06:00
explicit = any ( p . split ( "," )[ 0 ] > "1.0" for p in coordinates )
2024-05-08 09:46:31 -05:00
try :
self . _contour = np . array (
[
(
[ int ( p . split ( "," )[ 0 ]), int ( p . split ( "," )[ 1 ])]
if explicit
else [
int ( float ( p . split ( "," )[ 0 ]) * frame_shape [ 1 ]),
int ( float ( p . split ( "," )[ 1 ]) * frame_shape [ 0 ]),
]
)
for p in coordinates
]
)
except ValueError :
raise ValueError (
f "Invalid coordinates found in configuration file. Coordinates must be relative (between 0-1): { coordinates } "
)
2024-04-09 16:51:38 -06:00
if explicit :
self . coordinates = "," . join (
[
f ' { round ( int ( p . split ( "," )[ 0 ]) / frame_shape [ 1 ], 3 ) } , { round ( int ( p . split ( "," )[ 1 ]) / frame_shape [ 0 ], 3 ) } '
for p in coordinates
]
)
2021-05-22 18:28:15 -04:00
elif isinstance ( coordinates , str ):
points = coordinates . split ( "," )
2024-04-09 16:51:38 -06:00
explicit = any ( p > "1.0" for p in points )
2024-05-08 09:46:31 -05:00
try :
self . _contour = np . array (
[
(
[ int ( points [ i ]), int ( points [ i + 1 ])]
if explicit
else [
int ( float ( points [ i ]) * frame_shape [ 1 ]),
int ( float ( points [ i + 1 ]) * frame_shape [ 0 ]),
]
)
for i in range ( 0 , len ( points ), 2 )
]
)
except ValueError :
raise ValueError (
f "Invalid coordinates found in configuration file. Coordinates must be relative (between 0-1): { coordinates } "
)
2024-04-09 16:51:38 -06:00
if explicit :
self . coordinates = "," . join (
[
f " { round ( int ( points [ i ]) / frame_shape [ 1 ], 3 ) } , { round ( int ( points [ i + 1 ]) / frame_shape [ 0 ], 3 ) } "
for i in range ( 0 , len ( points ), 2 )
]
)
2021-05-22 18:28:15 -04:00
else :
2021-06-24 01:45:27 -04:00
self . _contour = np . array ([])
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class ObjectConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
track : List [ str ] = Field ( default = DEFAULT_TRACKED_OBJECTS , title = "Objects to track." )
2023-09-30 06:38:15 -06:00
filters : Dict [ str , FilterConfig ] = Field ( default = {}, title = "Object filters." )
2021-06-24 01:45:27 -04:00
mask : Union [ str , List [ str ]] = Field ( default = "" , title = "Object mask." )
2021-05-22 18:28:15 -04:00
2024-04-13 06:08:20 -06:00
class AlertsConfig ( FrigateBaseModel ):
"""Configure alerts"""
labels : List [ str ] = Field (
default = DEFAULT_ALERT_OBJECTS , title = "Labels to create alerts for."
)
2024-05-01 09:08:10 -05:00
required_zones : Union [ str , List [ str ]] = Field (
2024-04-13 06:08:20 -06:00
default_factory = list ,
title = "List of required zones to be entered in order to save the event as an alert." ,
)
2024-04-19 06:34:07 -05:00
@field_validator ( "required_zones" , mode = "before" )
@classmethod
def validate_required_zones ( cls , v ):
if isinstance ( v , str ) and "," not in v :
return [ v ]
return v
2024-04-13 06:08:20 -06:00
class DetectionsConfig ( FrigateBaseModel ):
"""Configure detections"""
labels : Optional [ List [ str ]] = Field (
default = None , title = "Labels to create detections for."
)
2024-05-01 09:08:10 -05:00
required_zones : Union [ str , List [ str ]] = Field (
2024-04-13 06:08:20 -06:00
default_factory = list ,
title = "List of required zones to be entered in order to save the event as a detection." ,
)
2024-04-19 06:34:07 -05:00
@field_validator ( "required_zones" , mode = "before" )
@classmethod
def validate_required_zones ( cls , v ):
if isinstance ( v , str ) and "," not in v :
return [ v ]
return v
2024-04-13 06:08:20 -06:00
class ReviewConfig ( FrigateBaseModel ):
"""Configure reviews"""
alerts : AlertsConfig = Field (
default_factory = AlertsConfig , title = "Review alerts config."
)
detections : DetectionsConfig = Field (
default_factory = DetectionsConfig , title = "Review detections config."
)
2023-07-01 07:18:33 -06:00
class AudioConfig ( FrigateBaseModel ):
enabled : bool = Field ( default = False , title = "Enable audio events." )
max_not_heard : int = Field (
default = 30 , title = "Seconds of not hearing the type of audio to end the event."
)
2023-07-08 06:16:24 -06:00
min_volume : int = Field (
default = 500 , title = "Min volume required to run audio detection."
)
2023-07-01 07:18:33 -06:00
listen : List [ str ] = Field (
default = DEFAULT_LISTEN_AUDIO , title = "Audio to listen for."
)
2024-02-29 16:10:13 -07:00
filters : Optional [ Dict [ str , AudioFilterConfig ]] = Field (
None , title = "Audio filters."
)
2023-07-01 07:18:33 -06:00
enabled_in_config : Optional [ bool ] = Field (
2024-02-29 16:10:13 -07:00
None , title = "Keep track of original state of audio detection."
2023-07-01 07:18:33 -06:00
)
2023-09-01 15:00:11 +03:00
num_threads : int = Field ( default = 2 , title = "Number of detection threads" , ge = 1 )
2023-07-01 07:18:33 -06:00
2021-06-24 01:45:27 -04:00
class BirdseyeModeEnum ( str , Enum ):
objects = "objects"
motion = "motion"
continuous = "continuous"
2023-10-26 04:20:55 -07:00
@classmethod
def get_index ( cls , type ):
return list ( cls ) . index ( type )
@classmethod
def get ( cls , index ):
return list ( cls )[ index ]
2021-06-24 01:45:27 -04:00
2024-02-10 10:55:13 -07:00
class BirdseyeLayoutConfig ( FrigateBaseModel ):
scaling_factor : float = Field (
default = 2.0 , title = "Birdseye Scaling Factor" , ge = 1.0 , le = 5.0
)
max_cameras : Optional [ int ] = Field ( default = None , title = "Max cameras" )
2021-09-04 16:56:01 -05:00
class BirdseyeConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = True , title = "Enable birdseye view." )
2023-01-16 16:50:35 -07:00
restream : bool = Field ( default = False , title = "Restream birdseye via RTSP." )
2021-06-24 01:45:27 -04:00
width : int = Field ( default = 1280 , title = "Birdseye width." )
height : int = Field ( default = 720 , title = "Birdseye height." )
2021-07-02 07:50:09 -05:00
quality : int = Field (
default = 8 ,
title = "Encoding quality." ,
ge = 1 ,
le = 31 ,
)
2024-02-10 10:55:13 -07:00
inactivity_threshold : int = Field (
default = 30 , title = "Birdseye Inactivity Threshold" , gt = 0
)
2021-06-24 01:45:27 -04:00
mode : BirdseyeModeEnum = Field (
default = BirdseyeModeEnum . objects , title = "Tracking mode."
2021-05-22 18:28:15 -04:00
)
2024-02-10 10:55:13 -07:00
layout : BirdseyeLayoutConfig = Field (
default_factory = BirdseyeLayoutConfig , title = "Birdseye Layout Config"
)
2021-06-11 07:26:35 -05:00
2022-04-16 08:44:04 -05:00
# uses BaseModel because some global attributes are not available at the camera level
class BirdseyeCameraConfig ( BaseModel ):
2022-04-15 05:59:30 -06:00
enabled : bool = Field ( default = True , title = "Enable birdseye view for camera." )
2023-04-27 02:29:01 +02:00
order : int = Field ( default = 0 , title = "Position of the camera in the birdseye view." )
2022-04-15 05:59:30 -06:00
mode : BirdseyeModeEnum = Field (
default = BirdseyeModeEnum . objects , title = "Tracking mode for camera."
)
2023-03-17 17:14:57 -05:00
# Note: Setting threads to less than 2 caused several issues with recording segments
# https://github.com/blakeblackshear/frigate/issues/5659
FFMPEG_GLOBAL_ARGS_DEFAULT = [ "-hide_banner" , "-loglevel" , "warning" , "-threads" , "2" ]
2022-12-16 10:41:03 -03:00
FFMPEG_INPUT_ARGS_DEFAULT = "preset-rtsp-generic"
2023-03-17 17:14:57 -05:00
DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT = [
"-threads" ,
"2" ,
"-f" ,
"rawvideo" ,
"-pix_fmt" ,
"yuv420p" ,
]
2022-12-16 10:41:03 -03:00
RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT = "preset-record-generic"
2020-11-01 06:17:44 -06:00
2021-09-04 16:56:01 -05:00
class FfmpegOutputArgsConfig ( FrigateBaseModel ):
2021-06-24 16:45:15 -04:00
detect : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default = DETECT_FFMPEG_OUTPUT_ARGS_DEFAULT ,
title = "Detect role FFmpeg output arguments." ,
)
2021-06-24 16:45:15 -04:00
record : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default = RECORD_FFMPEG_OUTPUT_ARGS_DEFAULT ,
title = "Record role FFmpeg output arguments." ,
)
2024-02-27 22:41:36 -05:00
_force_record_hvc1 : bool = PrivateAttr ( default = False )
2020-11-01 06:17:44 -06:00
2021-05-22 18:28:15 -04:00
2021-09-04 16:56:01 -05:00
class FfmpegConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
global_args : Union [ str , List [ str ]] = Field (
default = FFMPEG_GLOBAL_ARGS_DEFAULT , title = "Global FFmpeg arguments."
)
hwaccel_args : Union [ str , List [ str ]] = Field (
2024-01-01 09:36:40 -06:00
default = "auto" , title = "FFmpeg hardware acceleration arguments."
2021-06-24 01:45:27 -04:00
)
input_args : Union [ str , List [ str ]] = Field (
default = FFMPEG_INPUT_ARGS_DEFAULT , title = "FFmpeg input arguments."
)
output_args : FfmpegOutputArgsConfig = Field (
default_factory = FfmpegOutputArgsConfig ,
title = "FFmpeg output arguments per role." ,
2021-02-17 07:23:32 -06:00
)
2023-06-30 08:14:39 -04:00
retry_interval : float = Field (
default = 10.0 ,
title = "Time in seconds to wait before FFmpeg retries connecting to the camera." ,
)
2020-11-01 06:17:44 -06:00
2021-02-17 07:23:32 -06:00
2021-08-28 08:16:25 -05:00
class CameraRoleEnum ( str , Enum ):
2023-07-01 07:18:33 -06:00
audio = "audio"
2021-08-28 08:16:25 -05:00
record = "record"
detect = "detect"
2021-09-04 16:56:01 -05:00
class CameraInput ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
path : str = Field ( title = "Camera input path." )
2021-08-28 08:16:25 -05:00
roles : List [ CameraRoleEnum ] = Field ( title = "Roles assigned to this input." )
2021-06-24 16:45:15 -04:00
global_args : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default_factory = list , title = "FFmpeg global arguments."
)
2021-06-24 16:45:15 -04:00
hwaccel_args : Union [ str , List [ str ]] = Field (
2021-06-24 01:45:27 -04:00
default_factory = list , title = "FFmpeg hardware acceleration arguments."
)
2021-06-24 16:45:15 -04:00
input_args : Union [ str , List [ str ]] = Field (
default_factory = list , title = "FFmpeg input arguments."
)
2021-05-22 18:28:15 -04:00
2021-02-17 07:23:32 -06:00
2021-06-24 01:45:27 -04:00
class CameraFfmpegConfig ( FfmpegConfig ):
inputs : List [ CameraInput ] = Field ( title = "Camera inputs." )
2021-01-09 09:26:46 -08:00
2024-02-29 16:10:13 -07:00
@field_validator ( "inputs" )
@classmethod
2021-06-24 01:45:27 -04:00
def validate_roles ( cls , v ):
roles = [ role for i in v for role in i . roles ]
roles_set = set ( roles )
2021-01-09 09:26:46 -08:00
2021-06-24 01:45:27 -04:00
if len ( roles ) > len ( roles_set ):
raise ValueError ( "Each input role may only be used once." )
2023-05-29 12:31:17 +02:00
if "detect" not in roles :
2021-06-24 01:45:27 -04:00
raise ValueError ( "The detect role is required." )
return v
2021-02-17 07:23:32 -06:00
2021-09-04 16:56:01 -05:00
class SnapshotsConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = False , title = "Snapshots enabled." )
clean_copy : bool = Field (
default = True , title = "Create a clean copy of the snapshot image."
)
timestamp : bool = Field (
default = False , title = "Add a timestamp overlay on the snapshot."
)
bounding_box : bool = Field (
default = True , title = "Add a bounding box overlay on the snapshot."
)
crop : bool = Field ( default = False , title = "Crop the snapshot to the detected object." )
required_zones : List [ str ] = Field (
default_factory = list ,
title = "List of required zones to be entered in order to save a snapshot." ,
)
2024-02-29 16:10:13 -07:00
height : Optional [ int ] = Field ( None , title = "Snapshot image height." )
2021-06-24 01:45:27 -04:00
retain : RetainConfig = Field (
default_factory = RetainConfig , title = "Snapshot retention."
)
2021-07-02 07:47:03 -05:00
quality : int = Field (
default = 70 ,
title = "Quality of the encoded jpeg (0-100)." ,
ge = 0 ,
le = 100 ,
)
2021-06-15 22:19:49 +02:00
2021-09-04 16:56:01 -05:00
class ColorConfig ( FrigateBaseModel ):
2021-09-04 16:39:56 -05:00
red : int = Field ( default = 255 , ge = 0 , le = 255 , title = "Red" )
green : int = Field ( default = 255 , ge = 0 , le = 255 , title = "Green" )
blue : int = Field ( default = 255 , ge = 0 , le = 255 , title = "Blue" )
2020-11-29 15:55:53 -06:00
2021-02-17 07:23:32 -06:00
2021-09-04 16:34:48 -05:00
class TimestampPositionEnum ( str , Enum ):
tl = "tl"
tr = "tr"
bl = "bl"
br = "br"
class TimestampEffectEnum ( str , Enum ):
solid = "solid"
shadow = "shadow"
2021-09-04 16:56:01 -05:00
class TimestampStyleConfig ( FrigateBaseModel ):
2021-09-04 16:34:48 -05:00
position : TimestampPositionEnum = Field (
default = TimestampPositionEnum . tl , title = "Timestamp position."
)
2021-06-24 01:45:27 -04:00
format : str = Field ( default = DEFAULT_TIME_FORMAT , title = "Timestamp format." )
color : ColorConfig = Field ( default_factory = ColorConfig , title = "Timestamp color." )
thickness : int = Field ( default = 2 , title = "Timestamp thickness." )
2024-02-29 16:10:13 -07:00
effect : Optional [ TimestampEffectEnum ] = Field ( None , title = "Timestamp effect." )
2020-11-03 08:15:58 -06:00
2021-02-17 07:23:32 -06:00
2021-09-04 16:56:01 -05:00
class CameraMqttConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
enabled : bool = Field ( default = True , title = "Send image over MQTT." )
timestamp : bool = Field ( default = True , title = "Add timestamp to MQTT image." )
bounding_box : bool = Field ( default = True , title = "Add bounding box to MQTT image." )
crop : bool = Field ( default = True , title = "Crop MQTT image to detected object." )
height : int = Field ( default = 270 , title = "MQTT image height." )
required_zones : List [ str ] = Field (
default_factory = list ,
title = "List of required zones to be entered in order to send the image." ,
)
2021-07-02 07:47:03 -05:00
quality : int = Field (
default = 70 ,
title = "Quality of the encoded jpeg (0-100)." ,
ge = 0 ,
le = 100 ,
)
2021-06-23 08:09:15 -05:00
2023-01-16 16:50:35 -07:00
class CameraLiveConfig ( FrigateBaseModel ):
stream_name : str = Field ( default = "" , title = "Name of restream to use as live view." )
height : int = Field ( default = 720 , title = "Live camera view height" )
quality : int = Field ( default = 8 , ge = 1 , le = 31 , title = "Live camera view quality" )
2022-11-02 05:36:09 -06:00
2023-01-16 16:50:35 -07:00
class RestreamConfig ( BaseModel ):
2024-02-29 16:10:13 -07:00
model_config = ConfigDict ( extra = "allow" )
2021-06-24 01:45:27 -04:00
2022-04-15 06:23:02 -06:00
class CameraUiConfig ( FrigateBaseModel ):
order : int = Field ( default = 0 , title = "Order of camera in UI." )
2022-04-16 08:42:44 -05:00
dashboard : bool = Field (
default = True , title = "Show this camera in Frigate dashboard UI."
)
2022-04-15 06:23:02 -06:00
2021-09-04 16:56:01 -05:00
class CameraConfig ( FrigateBaseModel ):
2024-02-29 16:10:13 -07:00
name : Optional [ str ] = Field ( None , title = "Camera name." , pattern = REGEX_CAMERA_NAME )
2022-11-02 12:41:44 +01:00
enabled : bool = Field ( default = True , title = "Enable camera." )
2021-06-24 01:45:27 -04:00
ffmpeg : CameraFfmpegConfig = Field ( title = "FFmpeg configuration for the camera." )
best_image_timeout : int = Field (
default = 60 ,
title = "How long to wait for the image with the highest confidence score." ,
)
2023-11-01 14:08:59 +01:00
webui_url : Optional [ str ] = Field (
2024-02-29 16:10:13 -07:00
None ,
2023-11-01 14:08:59 +01:00
title = "URL to visit the camera directly from system page" ,
)
2021-06-24 01:45:27 -04:00
zones : Dict [ str , ZoneConfig ] = Field (
default_factory = dict , title = "Zone configuration."
)
record : RecordConfig = Field (
default_factory = RecordConfig , title = "Record configuration."
)
2023-01-16 16:50:35 -07:00
live : CameraLiveConfig = Field (
default_factory = CameraLiveConfig , title = "Live playback settings."
2021-08-28 09:14:00 -05:00
)
snapshots : SnapshotsConfig = Field (
default_factory = SnapshotsConfig , title = "Snapshot configuration."
2021-06-24 01:45:27 -04:00
)
mqtt : CameraMqttConfig = Field (
default_factory = CameraMqttConfig , title = "MQTT configuration."
)
objects : ObjectConfig = Field (
default_factory = ObjectConfig , title = "Object configuration."
)
2024-04-13 06:08:20 -06:00
review : ReviewConfig = Field (
default_factory = ReviewConfig , title = "Review configuration."
)
2023-07-01 07:18:33 -06:00
audio : AudioConfig = Field (
default_factory = AudioConfig , title = "Audio events configuration."
)
2024-02-29 16:10:13 -07:00
motion : Optional [ MotionConfig ] = Field (
None , title = "Motion detection configuration."
)
2021-08-28 09:14:00 -05:00
detect : DetectConfig = Field (
default_factory = DetectConfig , title = "Object detection configuration."
)
2023-04-26 05:08:53 -06:00
onvif : OnvifConfig = Field (
default_factory = OnvifConfig , title = "Camera Onvif Configuration."
)
2022-04-15 06:23:02 -06:00
ui : CameraUiConfig = Field (
default_factory = CameraUiConfig , title = "Camera UI Modifications."
)
2022-04-15 05:59:30 -06:00
birdseye : BirdseyeCameraConfig = Field (
default_factory = BirdseyeCameraConfig , title = "Birdseye camera configuration."
)
2021-06-24 17:02:46 -04:00
timestamp_style : TimestampStyleConfig = Field (
default_factory = TimestampStyleConfig , title = "Timestamp style configuration."
2021-06-24 01:45:27 -04:00
)
2021-11-08 07:32:29 -06:00
_ffmpeg_cmds : List [ Dict [ str , List [ str ]]] = PrivateAttr ()
2021-06-24 01:45:27 -04:00
def __init__ ( self , ** config ):
# Set zone colors
if "zones" in config :
2024-07-07 13:53:00 -05:00
colors = generate_color_palette ( len ( config [ "zones" ]))
2021-06-24 01:45:27 -04:00
config [ "zones" ] = {
2024-07-07 13:53:00 -05:00
name : { ** z , "color" : color }
for ( name , z ), color in zip ( config [ "zones" ] . items (), colors )
2021-06-24 01:45:27 -04:00
}
2021-10-16 07:46:39 -05:00
# add roles to the input if there is only one
if len ( config [ "ffmpeg" ][ "inputs" ]) == 1 :
2023-07-01 07:18:33 -06:00
has_audio = "audio" in config [ "ffmpeg" ][ "inputs" ][ 0 ] . get ( "roles" , [])
2022-11-02 05:36:09 -06:00
config [ "ffmpeg" ][ "inputs" ][ 0 ][ "roles" ] = [
"record" ,
"detect" ,
]
2023-07-01 07:18:33 -06:00
if has_audio :
config [ "ffmpeg" ][ "inputs" ][ 0 ][ "roles" ] . append ( "audio" )
2021-06-24 01:45:27 -04:00
super () . __init__ ( ** config )
2020-11-03 08:15:58 -06:00
2021-05-22 18:28:15 -04:00
@property
def frame_shape ( self ) -> Tuple [ int , int ]:
2021-08-14 14:18:35 -05:00
return self . detect . height , self . detect . width
2021-05-22 18:28:15 -04:00
@property
def frame_shape_yuv ( self ) -> Tuple [ int , int ]:
2021-08-14 14:18:35 -05:00
return self . detect . height * 3 // 2 , self . detect . width
2021-05-22 18:28:15 -04:00
@property
def ffmpeg_cmds ( self ) -> List [ Dict [ str , List [ str ]]]:
2021-11-08 07:32:29 -06:00
return self . _ffmpeg_cmds
2021-11-08 19:05:39 -06:00
def create_ffmpeg_cmds ( self ):
2022-01-14 16:31:25 +03:00
if "_ffmpeg_cmds" in self :
return
2021-05-22 18:28:15 -04:00
ffmpeg_cmds = []
for ffmpeg_input in self . ffmpeg . inputs :
2020-12-07 08:07:35 -06:00
ffmpeg_cmd = self . _get_ffmpeg_cmd ( ffmpeg_input )
if ffmpeg_cmd is None :
continue
2021-05-22 18:28:15 -04:00
ffmpeg_cmds . append ({ "roles" : ffmpeg_input . roles , "cmd" : ffmpeg_cmd })
2021-11-08 18:20:47 -06:00
self . _ffmpeg_cmds = ffmpeg_cmds
2020-11-03 08:15:58 -06:00
2021-06-24 01:45:27 -04:00
def _get_ffmpeg_cmd ( self , ffmpeg_input : CameraInput ):
2020-11-29 15:55:53 -06:00
ffmpeg_output_args = []
2021-02-17 07:23:32 -06:00
if "detect" in ffmpeg_input . roles :
2022-11-28 20:48:11 -07:00
detect_args = get_ffmpeg_arg_list ( self . ffmpeg . output_args . detect )
2022-12-30 09:56:52 -07:00
scale_detect_args = parse_preset_hardware_acceleration_scale (
ffmpeg_input . hwaccel_args or self . ffmpeg . hwaccel_args ,
detect_args ,
self . detect . fps ,
self . detect . width ,
self . detect . height ,
2021-08-14 14:18:35 -05:00
)
2022-12-30 09:56:52 -07:00
ffmpeg_output_args = scale_detect_args + ffmpeg_output_args + [ "pipe:" ]
2022-11-28 20:48:11 -07:00
2021-08-15 08:30:27 -05:00
if "record" in ffmpeg_input . roles and self . record . enabled :
2022-11-28 20:48:11 -07:00
record_args = get_ffmpeg_arg_list (
2024-02-27 22:41:36 -05:00
parse_preset_output_record (
self . ffmpeg . output_args . record ,
self . ffmpeg . output_args . _force_record_hvc1 ,
)
2022-11-28 20:48:11 -07:00
or self . ffmpeg . output_args . record
2021-06-24 16:45:15 -04:00
)
2021-10-16 10:36:13 -05:00
2021-06-24 16:45:15 -04:00
ffmpeg_output_args = (
record_args
2023-11-05 13:30:29 -07:00
+ [ f " { os . path . join ( CACHE_DIR , self . name ) } @ { CACHE_SEGMENT_FORMAT } .mp4" ]
2021-02-17 07:23:32 -06:00
+ ffmpeg_output_args
)
2021-01-09 09:26:46 -08:00
2020-12-07 08:07:35 -06:00
# if there arent any outputs enabled for this input
if len ( ffmpeg_output_args ) == 0 :
return None
2022-11-28 20:48:11 -07:00
global_args = get_ffmpeg_arg_list (
ffmpeg_input . global_args or self . ffmpeg . global_args
2021-06-24 16:45:15 -04:00
)
2024-02-14 17:24:36 -07:00
camera_arg = (
self . ffmpeg . hwaccel_args if self . ffmpeg . hwaccel_args != "auto" else None
)
2022-11-28 20:48:11 -07:00
hwaccel_args = get_ffmpeg_arg_list (
2023-07-26 06:50:41 -04:00
parse_preset_hardware_acceleration_decode (
ffmpeg_input . hwaccel_args ,
self . detect . fps ,
self . detect . width ,
self . detect . height ,
)
2022-12-30 09:56:52 -07:00
or ffmpeg_input . hwaccel_args
2023-07-26 06:50:41 -04:00
or parse_preset_hardware_acceleration_decode (
2024-02-14 17:24:36 -07:00
camera_arg ,
2023-07-26 06:50:41 -04:00
self . detect . fps ,
self . detect . width ,
self . detect . height ,
)
2024-02-14 17:24:36 -07:00
or camera_arg
or []
2021-06-24 16:45:15 -04:00
)
2022-11-28 20:48:11 -07:00
input_args = get_ffmpeg_arg_list (
2022-12-30 09:56:52 -07:00
parse_preset_input ( ffmpeg_input . input_args , self . detect . fps )
or ffmpeg_input . input_args
2022-11-28 20:48:11 -07:00
or parse_preset_input ( self . ffmpeg . input_args , self . detect . fps )
or self . ffmpeg . input_args
2021-06-24 16:45:15 -04:00
)
2021-02-17 07:23:32 -06:00
cmd = (
[ "ffmpeg" ]
2021-06-24 16:45:15 -04:00
+ global_args
+ hwaccel_args
+ input_args
2022-11-02 06:00:54 -06:00
+ [ "-i" , escape_special_characters ( ffmpeg_input . path )]
2021-02-17 07:23:32 -06:00
+ ffmpeg_output_args
)
2021-01-09 09:26:46 -08:00
2021-02-17 07:23:32 -06:00
return [ part for part in cmd if part != "" ]
2021-01-09 09:26:46 -08:00
2021-06-24 01:45:27 -04:00
2021-09-04 16:56:01 -05:00
class DatabaseConfig ( FrigateBaseModel ):
2023-04-23 13:35:40 -03:00
path : str = Field ( default = DEFAULT_DB_PATH , title = "Database path." )
2020-11-17 21:11:19 -06:00
2021-06-24 01:45:27 -04:00
class LogLevelEnum ( str , Enum ):
debug = "debug"
info = "info"
warning = "warning"
error = "error"
critical = "critical"
2021-09-04 16:56:01 -05:00
class LoggerConfig ( FrigateBaseModel ):
2021-06-24 01:45:27 -04:00
default : LogLevelEnum = Field (
default = LogLevelEnum . info , title = "Default logging level."
)
logs : Dict [ str , LogLevelEnum ] = Field (
default_factory = dict , title = "Log level for specified processes."
)
2024-03-04 16:18:30 -07:00
class CameraGroupConfig ( FrigateBaseModel ):
"""Represents a group of cameras."""
2024-05-29 09:19:05 -05:00
cameras : Union [ str , List [ str ]] = Field (
2024-03-04 16:18:30 -07:00
default_factory = list , title = "List of cameras in this group."
)
icon : str = Field ( default = "generic" , title = "Icon that represents camera group." )
order : int = Field ( default = 0 , title = "Sort order for group." )
2024-05-29 09:19:05 -05:00
@field_validator ( "cameras" , mode = "before" )
@classmethod
def validate_cameras ( cls , v ):
if isinstance ( v , str ) and "," not in v :
return [ v ]
return v
2024-03-04 16:18:30 -07:00
2022-11-08 18:47:45 -07:00
def verify_config_roles ( camera_config : CameraConfig ) -> None :
"""Verify that roles are setup in the config correctly."""
assigned_roles = list (
set ([ r for i in camera_config . ffmpeg . inputs for r in i . roles ])
)
2023-05-29 12:31:17 +02:00
if camera_config . record . enabled and "record" not in assigned_roles :
2022-11-08 18:47:45 -07:00
raise ValueError (
f "Camera { camera_config . name } has record enabled, but record is not assigned to an input."
)
2023-07-01 07:18:33 -06:00
if camera_config . audio . enabled and "audio" not in assigned_roles :
raise ValueError (
f "Camera { camera_config . name } has audio events enabled, but audio is not assigned to an input."
)
2023-01-16 16:50:35 -07:00
def verify_valid_live_stream_name (
frigate_config : FrigateConfig , camera_config : CameraConfig
2023-04-24 07:24:28 -05:00
) -> ValueError | None :
2023-01-16 16:50:35 -07:00
"""Verify that a restream exists to use for live view."""
if (
camera_config . live . stream_name
2024-02-29 16:10:13 -07:00
not in frigate_config . go2rtc . model_dump () . get ( "streams" , {}) . keys ()
2023-01-16 16:50:35 -07:00
):
return ValueError (
f "No restream with name { camera_config . live . stream_name } exists for camera { camera_config . name } ."
2022-11-08 18:47:45 -07:00
)
def verify_recording_retention ( camera_config : CameraConfig ) -> None :
"""Verify that recording retention modes are ranked correctly."""
rank_map = {
RetainModeEnum . all : 0 ,
RetainModeEnum . motion : 1 ,
RetainModeEnum . active_objects : 2 ,
}
if (
camera_config . record . retain . days != 0
and rank_map [ camera_config . record . retain . mode ]
> rank_map [ camera_config . record . events . retain . mode ]
):
logger . warning (
f " { camera_config . name } : Recording retention is configured for { camera_config . record . retain . mode } and event retention is configured for { camera_config . record . events . retain . mode } . The more restrictive retention policy will be applied."
)
2022-12-08 20:03:54 -07:00
def verify_recording_segments_setup_with_reasonable_time (
camera_config : CameraConfig ,
) -> None :
"""Verify that recording segments are setup and segment time is not greater than 60."""
record_args : list [ str ] = get_ffmpeg_arg_list (
camera_config . ffmpeg . output_args . record
)
2022-12-09 07:35:28 -07:00
if record_args [ 0 ] . startswith ( "preset" ):
return
2022-12-08 20:03:54 -07:00
seg_arg_index = record_args . index ( "-segment_time" )
if seg_arg_index < 0 :
raise ValueError (
f "Camera { camera_config . name } has no segment_time in recording output args, segment args are required for record."
)
if int ( record_args [ seg_arg_index + 1 ]) > 60 :
raise ValueError (
f "Camera { camera_config . name } has invalid segment_time output arg, segment_time must be 60 or less."
)
2022-11-08 18:47:45 -07:00
def verify_zone_objects_are_tracked ( camera_config : CameraConfig ) -> None :
"""Verify that user has not entered zone objects that are not in the tracking config."""
for zone_name , zone in camera_config . zones . items ():
for obj in zone . objects :
if obj not in camera_config . objects . track :
raise ValueError (
f "Zone { zone_name } is configured to track { obj } but that object type is not added to objects -> track."
)
2024-04-18 10:35:16 -06:00
def verify_required_zones_exist ( camera_config : CameraConfig ) -> None :
for det_zone in camera_config . review . detections . required_zones :
if det_zone not in camera_config . zones . keys ():
raise ValueError (
f "Camera { camera_config . name } has a required zone for detections { det_zone } that is not defined."
)
for det_zone in camera_config . review . alerts . required_zones :
if det_zone not in camera_config . zones . keys ():
raise ValueError (
f "Camera { camera_config . name } has a required zone for alerts { det_zone } that is not defined."
)
2023-07-08 07:04:47 -05:00
def verify_autotrack_zones ( camera_config : CameraConfig ) -> ValueError | None :
"""Verify that required_zones are specified when autotracking is enabled."""
if (
camera_config . onvif . autotracking . enabled
and not camera_config . onvif . autotracking . required_zones
):
raise ValueError (
f "Camera { camera_config . name } has autotracking enabled, required_zones must be set to at least one of the camera's zones."
)
2024-02-19 06:26:59 -07:00
def verify_motion_and_detect ( camera_config : CameraConfig ) -> ValueError | None :
"""Verify that required_zones are specified when autotracking is enabled."""
if camera_config . detect . enabled and not camera_config . motion . enabled :
raise ValueError (
f "Camera { camera_config . name } has motion detection disabled and object detection enabled but object detection requires motion detection."
)
2021-09-04 16:56:01 -05:00
class FrigateConfig ( FrigateBaseModel ):
2024-05-18 11:36:13 -05:00
mqtt : MqttConfig = Field ( title = "MQTT configuration." )
2021-06-24 01:45:27 -04:00
database : DatabaseConfig = Field (
default_factory = DatabaseConfig , title = "Database configuration."
)
2024-06-02 07:48:28 -05:00
tls : TlsConfig = Field ( default_factory = TlsConfig , title = "TLS configuration." )
2024-06-14 18:02:13 -05:00
proxy : ProxyConfig = Field (
default_factory = ProxyConfig , title = "Proxy configuration."
)
2024-05-18 11:36:13 -05:00
auth : AuthConfig = Field ( default_factory = AuthConfig , title = "Auth configuration." )
2021-06-24 01:45:27 -04:00
environment_vars : Dict [ str , str ] = Field (
default_factory = dict , title = "Frigate environment variables."
)
2022-02-27 08:04:12 -06:00
ui : UIConfig = Field ( default_factory = UIConfig , title = "UI configuration." )
2023-01-26 00:36:26 +00:00
telemetry : TelemetryConfig = Field (
default_factory = TelemetryConfig , title = "Telemetry configuration."
)
2021-06-24 01:45:27 -04:00
model : ModelConfig = Field (
default_factory = ModelConfig , title = "Detection model configuration."
)
2023-04-24 07:24:28 -05:00
detectors : Dict [ str , BaseDetectorConfig ] = Field (
2022-12-15 07:12:52 -06:00
default = DEFAULT_DETECTORS ,
2021-06-24 01:45:27 -04:00
title = "Detector hardware configuration." ,
)
logger : LoggerConfig = Field (
default_factory = LoggerConfig , title = "Logging configuration."
)
record : RecordConfig = Field (
default_factory = RecordConfig , title = "Global record configuration."
)
snapshots : SnapshotsConfig = Field (
default_factory = SnapshotsConfig , title = "Global snapshots configuration."
)
2023-01-16 16:50:35 -07:00
live : CameraLiveConfig = Field (
default_factory = CameraLiveConfig , title = "Live playback settings."
)
go2rtc : RestreamConfig = Field (
2022-11-02 05:36:09 -06:00
default_factory = RestreamConfig , title = "Global restream configuration."
)
2021-06-24 01:45:27 -04:00
birdseye : BirdseyeConfig = Field (
default_factory = BirdseyeConfig , title = "Birdseye configuration."
)
ffmpeg : FfmpegConfig = Field (
default_factory = FfmpegConfig , title = "Global FFmpeg configuration."
)
objects : ObjectConfig = Field (
default_factory = ObjectConfig , title = "Global object configuration."
)
2024-04-13 06:08:20 -06:00
review : ReviewConfig = Field (
default_factory = ReviewConfig , title = "Review configuration."
)
2023-07-01 07:18:33 -06:00
audio : AudioConfig = Field (
default_factory = AudioConfig , title = "Global Audio events configuration."
)
2021-06-24 01:45:27 -04:00
motion : Optional [ MotionConfig ] = Field (
2024-02-29 16:10:13 -07:00
None , title = "Global motion detection configuration."
2021-06-24 01:45:27 -04:00
)
2021-08-28 09:14:00 -05:00
detect : DetectConfig = Field (
default_factory = DetectConfig , title = "Global object tracking configuration."
2021-06-24 01:45:27 -04:00
)
cameras : Dict [ str , CameraConfig ] = Field ( title = "Camera configuration." )
2024-03-04 16:18:30 -07:00
camera_groups : Dict [ str , CameraGroupConfig ] = Field (
2024-03-05 05:02:34 -07:00
default_factory = dict , title = "Camera group configuration"
2024-03-04 16:18:30 -07:00
)
2021-09-03 05:03:36 -07:00
timestamp_style : TimestampStyleConfig = Field (
default_factory = TimestampStyleConfig ,
title = "Global timestamp style configuration." ,
)
2024-05-26 15:49:12 -06:00
version : Optional [ float ] = Field ( default = None , title = "Current config version." )
2021-06-24 01:45:27 -04:00
2023-04-30 13:32:36 -05:00
def runtime_config ( self , plus_api : PlusApi = None ) -> FrigateConfig :
2021-06-24 01:45:27 -04:00
"""Merge camera config with globals."""
2024-02-29 16:10:13 -07:00
config = self . model_copy ( deep = True )
2021-06-24 16:53:01 -04:00
2024-06-14 18:02:13 -05:00
# Proxy secret substitution
if config . proxy . auth_secret :
config . proxy . auth_secret = config . proxy . auth_secret . format (
** FRIGATE_ENV_VARS
)
2023-01-07 18:10:48 -05:00
# MQTT user/password substitutions
if config . mqtt . user or config . mqtt . password :
config . mqtt . user = config . mqtt . user . format ( ** FRIGATE_ENV_VARS )
2021-06-24 16:53:01 -04:00
config . mqtt . password = config . mqtt . password . format ( ** FRIGATE_ENV_VARS )
2023-09-30 06:38:15 -06:00
# set default min_score for object attributes
for attribute in ALL_ATTRIBUTE_LABELS :
if not config . objects . filters . get ( attribute ):
config . objects . filters [ attribute ] = FilterConfig ( min_score = 0.7 )
elif config . objects . filters [ attribute ] . min_score == 0.5 :
config . objects . filters [ attribute ] . min_score = 0.7
2024-01-01 09:36:40 -06:00
# auto detect hwaccel args
if config . ffmpeg . hwaccel_args == "auto" :
config . ffmpeg . hwaccel_args = auto_detect_hwaccel ()
2022-11-02 12:41:44 +01:00
# Global config to propagate down to camera level
2024-02-29 16:10:13 -07:00
global_config = config . model_dump (
2021-06-24 01:45:27 -04:00
include = {
2023-07-01 07:18:33 -06:00
"audio" : ... ,
2022-04-15 05:59:30 -06:00
"birdseye" : ... ,
2021-06-24 01:45:27 -04:00
"record" : ... ,
"snapshots" : ... ,
2023-01-16 16:50:35 -07:00
"live" : ... ,
2021-06-24 01:45:27 -04:00
"objects" : ... ,
2024-04-13 06:08:20 -06:00
"review" : ... ,
2021-06-24 01:45:27 -04:00
"motion" : ... ,
"detect" : ... ,
"ffmpeg" : ... ,
2021-09-03 05:03:36 -07:00
"timestamp_style" : ... ,
2021-05-22 18:28:15 -04:00
},
2021-06-24 01:45:27 -04:00
exclude_unset = True ,
2021-05-22 18:28:15 -04:00
)
2021-06-24 01:45:27 -04:00
for name , camera in config . cameras . items ():
2024-02-29 16:10:13 -07:00
merged_config = deep_merge (
2024-04-17 06:02:59 -06:00
camera . model_dump ( exclude_unset = True ), global_config
2024-02-29 16:10:13 -07:00
)
camera_config : CameraConfig = CameraConfig . model_validate (
2021-07-09 16:14:16 -04:00
{ "name" : name , ** merged_config }
)
2021-05-22 18:28:15 -04:00
2024-02-01 05:44:10 -07:00
if camera_config . ffmpeg . hwaccel_args == "auto" :
camera_config . ffmpeg . hwaccel_args = config . ffmpeg . hwaccel_args
2024-02-27 22:41:36 -05:00
for input in camera_config . ffmpeg . inputs :
2024-03-05 07:11:08 -07:00
need_record_fourcc = False and "record" in input . roles
2024-02-27 22:41:36 -05:00
need_detect_dimensions = "detect" in input . roles and (
camera_config . detect . height is None
or camera_config . detect . width is None
)
2023-07-14 14:56:03 +03:00
2024-02-27 22:41:36 -05:00
if need_detect_dimensions or need_record_fourcc :
stream_info = { "width" : 0 , "height" : 0 , "fourcc" : None }
try :
2024-05-29 07:41:41 -06:00
stream_info = stream_info_retriever . get_stream_info ( input . path )
2024-02-27 22:41:36 -05:00
except Exception :
logger . warn (
f "Error detecting stream parameters automatically for { input . path } Applying default values."
2023-07-14 14:56:03 +03:00
)
2024-02-27 22:41:36 -05:00
stream_info = { "width" : 0 , "height" : 0 , "fourcc" : None }
if need_detect_dimensions :
camera_config . detect . width = (
stream_info [ "width" ]
if stream_info . get ( "width" )
else DEFAULT_DETECT_DIMENSIONS [ "width" ]
)
camera_config . detect . height = (
stream_info [ "height" ]
if stream_info . get ( "height" )
else DEFAULT_DETECT_DIMENSIONS [ "height" ]
)
if need_record_fourcc :
# Apple only supports HEVC if it is hvc1 (vs. hev1)
camera_config . ffmpeg . output_args . _force_record_hvc1 = (
stream_info [ "fourcc" ] == "hevc"
if stream_info . get ( "hevc" )
else False
)
2023-07-14 14:56:03 +03:00
2024-05-19 22:36:41 -05:00
# Warn if detect fps > 10
if camera_config . detect . fps > 10 :
logger . warning (
f " { camera_config . name } detect fps is set to { camera_config . detect . fps } . This does NOT need to match your camera's frame rate. High values could lead to reduced performance. Recommended value is 5."
)
2023-10-30 18:26:31 -06:00
# Default min_initialized configuration
2024-02-29 16:10:13 -07:00
min_initialized = int ( camera_config . detect . fps / 2 )
2023-10-30 18:26:31 -06:00
if camera_config . detect . min_initialized is None :
camera_config . detect . min_initialized = min_initialized
2021-08-28 09:14:00 -05:00
# Default max_disappeared configuration
2021-08-28 08:51:29 -05:00
max_disappeared = camera_config . detect . fps * 5
if camera_config . detect . max_disappeared is None :
camera_config . detect . max_disappeared = max_disappeared
2022-02-11 07:12:51 -06:00
# Default stationary_threshold configuration
2022-02-11 07:30:47 -06:00
stationary_threshold = camera_config . detect . fps * 10
2022-02-13 08:58:44 -06:00
if camera_config . detect . stationary . threshold is None :
camera_config . detect . stationary . threshold = stationary_threshold
2023-06-16 07:32:43 -05:00
# default to the stationary_threshold if not defined
if camera_config . detect . stationary . interval is None :
camera_config . detect . stationary . interval = stationary_threshold
2022-02-11 07:12:51 -06:00
2021-06-24 16:53:01 -04:00
# FFMPEG input substitution
for input in camera_config . ffmpeg . inputs :
input . path = input . path . format ( ** FRIGATE_ENV_VARS )
2023-04-26 05:08:53 -06:00
# ONVIF substitution
if camera_config . onvif . user or camera_config . onvif . password :
camera_config . onvif . user = camera_config . onvif . user . format (
** FRIGATE_ENV_VARS
)
camera_config . onvif . password = camera_config . onvif . password . format (
** FRIGATE_ENV_VARS
)
2023-07-01 07:18:33 -06:00
# set config pre-value
camera_config . audio . enabled_in_config = camera_config . audio . enabled
2024-02-19 06:26:59 -07:00
camera_config . record . enabled_in_config = camera_config . record . enabled
2023-11-15 19:25:48 -06:00
camera_config . onvif . autotracking . enabled_in_config = (
camera_config . onvif . autotracking . enabled
)
2023-04-26 05:08:53 -06:00
2021-06-24 01:45:27 -04:00
# Add default filters
object_keys = camera_config . objects . track
if camera_config . objects . filters is None :
camera_config . objects . filters = {}
object_keys = object_keys - camera_config . objects . filters . keys ()
for key in object_keys :
camera_config . objects . filters [ key ] = FilterConfig ()
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
# Apply global object masks and convert masks to numpy array
for object , filter in camera_config . objects . filters . items ():
if camera_config . objects . mask :
filter_mask = []
if filter . mask is not None :
filter_mask = (
filter . mask
if isinstance ( filter . mask , list )
else [ filter . mask ]
)
object_mask = (
2024-04-18 10:35:16 -06:00
get_relative_coordinates (
(
camera_config . objects . mask
if isinstance ( camera_config . objects . mask , list )
else [ camera_config . objects . mask ]
),
camera_config . frame_shape ,
)
or []
2021-06-24 01:45:27 -04:00
)
filter . mask = filter_mask + object_mask
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
# Set runtime filter to create masks
camera_config . objects . filters [ object ] = RuntimeFilterConfig (
frame_shape = camera_config . frame_shape ,
2024-04-17 06:02:59 -06:00
** filter . model_dump ( exclude_unset = True ),
2021-06-24 01:45:27 -04:00
)
2021-05-22 18:28:15 -04:00
2021-06-24 01:45:27 -04:00
# Convert motion configuration
if camera_config . motion is None :
camera_config . motion = RuntimeMotionConfig (
frame_shape = camera_config . frame_shape
)
else :
camera_config . motion = RuntimeMotionConfig (
frame_shape = camera_config . frame_shape ,
raw_mask = camera_config . motion . mask ,
2024-04-17 06:02:59 -06:00
** camera_config . motion . model_dump ( exclude_unset = True ),
2021-06-24 01:45:27 -04:00
)
2024-02-19 06:26:59 -07:00
camera_config . motion . enabled_in_config = camera_config . motion . enabled
2021-05-22 18:28:15 -04:00
2024-04-09 16:51:38 -06:00
# generate zone contours
if len ( camera_config . zones ) > 0 :
for zone in camera_config . zones . values ():
zone . generate_contour ( camera_config . frame_shape )
2023-01-16 16:50:35 -07:00
# Set live view stream if none is set
if not camera_config . live . stream_name :
camera_config . live . stream_name = name
2022-11-08 18:47:45 -07:00
verify_config_roles ( camera_config )
2023-01-16 16:50:35 -07:00
verify_valid_live_stream_name ( config , camera_config )
2022-11-08 18:47:45 -07:00
verify_recording_retention ( camera_config )
2022-12-08 20:03:54 -07:00
verify_recording_segments_setup_with_reasonable_time ( camera_config )
2022-11-08 18:47:45 -07:00
verify_zone_objects_are_tracked ( camera_config )
2024-04-18 10:35:16 -06:00
verify_required_zones_exist ( camera_config )
2023-07-08 07:04:47 -05:00
verify_autotrack_zones ( camera_config )
2024-02-19 06:26:59 -07:00
verify_motion_and_detect ( camera_config )
2021-10-22 19:41:28 -05:00
2022-11-02 12:41:44 +01:00
# generate the ffmpeg commands
2022-01-14 16:31:25 +03:00
camera_config . create_ffmpeg_cmds ()
2021-10-24 13:33:13 -05:00
config . cameras [ name ] = camera_config
2022-12-15 07:12:52 -06:00
2023-01-06 18:31:54 -07:00
# get list of unique enabled labels for tracking
enabled_labels = set ( config . objects . track )
for _ , camera in config . cameras . items ():
enabled_labels . update ( camera . objects . track )
2023-01-07 11:05:11 -07:00
config . model . create_colormap ( sorted ( enabled_labels ))
2023-04-30 13:32:36 -05:00
config . model . check_and_load_plus_model ( plus_api )
2023-01-06 18:31:54 -07:00
2022-12-15 07:12:52 -06:00
for key , detector in config . detectors . items ():
2024-02-29 16:10:13 -07:00
adapter = TypeAdapter ( DetectorConfig )
model_dict = (
2024-05-04 09:15:03 -06:00
detector
if isinstance ( detector , dict )
else detector . model_dump ( warnings = "none" )
2024-02-29 16:10:13 -07:00
)
detector_config : DetectorConfig = adapter . validate_python ( model_dict )
2022-12-15 07:12:52 -06:00
if detector_config . model is None :
2024-05-04 09:15:03 -06:00
detector_config . model = config . model . model_copy ()
2022-12-15 07:12:52 -06:00
else :
2024-05-04 09:15:03 -06:00
path = detector_config . model . path
detector_config . model = config . model . model_copy ()
detector_config . model . path = path
if "path" not in model_dict or len ( model_dict . keys ()) > 1 :
2022-12-15 07:12:52 -06:00
logger . warning (
"Customizing more than a detector model path is unsupported."
)
2024-05-04 09:15:03 -06:00
2022-12-15 07:12:52 -06:00
merged_model = deep_merge (
2024-05-04 09:15:03 -06:00
detector_config . model . model_dump ( exclude_unset = True , warnings = "none" ),
config . model . model_dump ( exclude_unset = True , warnings = "none" ),
2022-12-15 07:12:52 -06:00
)
2023-04-24 07:24:28 -05:00
2023-05-29 12:31:17 +02:00
if "path" not in merged_model :
2023-04-24 07:24:28 -05:00
if detector_config . type == "cpu" :
merged_model [ "path" ] = "/cpu_model.tflite"
elif detector_config . type == "edgetpu" :
merged_model [ "path" ] = "/edgetpu_model.tflite"
2024-02-29 16:10:13 -07:00
detector_config . model = ModelConfig . model_validate ( merged_model )
2023-04-30 13:32:36 -05:00
detector_config . model . check_and_load_plus_model (
plus_api , detector_config . type
)
2023-04-24 07:24:28 -05:00
detector_config . model . compute_model_hash ()
2022-12-15 07:12:52 -06:00
config . detectors [ key ] = detector_config
2020-11-03 08:15:58 -06:00
return config
2024-02-29 16:10:13 -07:00
@field_validator ( "cameras" )
@classmethod
2021-06-24 01:45:27 -04:00
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 ():
raise ValueError ( "Zones cannot share names with cameras" )
return v
@classmethod
def parse_file ( cls , config_file ):
2020-11-03 08:15:58 -06:00
with open ( config_file ) as f :
raw_config = f . read ()
2021-01-09 09:26:46 -08:00
2021-12-12 10:27:05 -05:00
if config_file . endswith ( YAML_EXT ):
2022-11-08 18:47:45 -07:00
config = load_config_with_no_duplicates ( raw_config )
2020-11-03 08:15:58 -06:00
elif config_file . endswith ( ".json" ):
config = json . loads ( raw_config )
2021-01-09 09:26:46 -08:00
2024-02-29 16:10:13 -07:00
return cls . model_validate ( config )
2022-12-07 06:36:56 -07:00
@classmethod
def parse_raw ( cls , raw_config ):
config = load_config_with_no_duplicates ( raw_config )
2024-02-29 16:10:13 -07:00
return cls . model_validate ( config )