from enum import Enum from typing import Optional, Union from pydantic import Field, field_validator from ..base import FrigateBaseModel from ..env import EnvString from .objects import DEFAULT_TRACKED_OBJECTS __all__ = ["OnvifConfig", "PtzAutotrackConfig", "ZoomingModeEnum"] class ZoomingModeEnum(str, Enum): disabled = "disabled" absolute = "absolute" relative = "relative" class PtzAutotrackConfig(FrigateBaseModel): enabled: bool = Field( default=False, title="Enable Autotracking", description="Enable or disable automatic PTZ camera tracking of detected objects.", ) calibrate_on_startup: bool = Field( default=False, title="Calibrate on start", description="Measure PTZ motor speeds on startup to improve tracking accuracy. Frigate will update config with movement_weights after calibration.", ) zooming: ZoomingModeEnum = Field( default=ZoomingModeEnum.disabled, title="Zoom mode", description="Control zoom behavior: disabled (pan/tilt only), absolute (most compatible), or relative (concurrent pan/tilt/zoom).", ) zoom_factor: float = Field( default=0.3, title="Zoom factor", description="Control zoom level on tracked objects. Lower values keep more scene in view; higher values zoom in closer but may lose tracking. Values between 0.1 and 0.75.", ge=0.1, le=0.75, ) track: list[str] = Field( default=DEFAULT_TRACKED_OBJECTS, title="Tracked objects", description="List of object types that should trigger autotracking.", ) required_zones: list[str] = Field( default_factory=list, title="Required zones", description="Objects must enter one of these zones before autotracking begins.", ) return_preset: str = Field( default="home", title="Return preset", description="ONVIF preset name configured in camera firmware to return to after tracking ends.", ) timeout: int = Field( default=10, 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( default_factory=list, title="Movement weights", description="Calibration values automatically generated by camera calibration. Do not modify manually.", ) enabled_in_config: Optional[bool] = Field( default=None, title="Original autotrack state", description="Internal field to track whether autotracking was enabled in configuration.", ) @field_validator("movement_weights", mode="before") @classmethod def validate_weights(cls, v): if v is None: return None if isinstance(v, str): weights = list(map(str, map(float, v.split(",")))) elif isinstance(v, list): weights = [str(float(val)) for val in v] else: raise ValueError("Invalid type for movement_weights") if len(weights) != 6: raise ValueError( "movement_weights must have exactly 6 floats, remove this line from your config and run autotracking calibration" ) return weights class OnvifConfig(FrigateBaseModel): host: EnvString = Field( default="", title="ONVIF host", description="Host (and optional scheme) for the ONVIF service for this camera.", ) port: int = Field( default=8000, title="ONVIF port", description="Port number for the ONVIF service.", ) user: Optional[EnvString] = Field( default=None, title="ONVIF username", description="Username for ONVIF authentication; some devices require admin user for ONVIF.", ) password: Optional[EnvString] = Field( default=None, title="ONVIF password", description="Password for ONVIF authentication.", ) tls_insecure: bool = Field( default=False, title="Disable TLS verify", description="Skip TLS verification and disable digest auth for ONVIF (unsafe; use in safe networks only).", ) autotracking: PtzAutotrackConfig = Field( default_factory=PtzAutotrackConfig, title="Autotracking", description="Automatically track moving objects and keep them centered in the frame using PTZ camera movements.", ) ignore_time_mismatch: bool = Field( default=False, title="Ignore time mismatch", description="Ignore time synchronization differences between camera and Frigate server for ONVIF communication.", )