{ pkgs, lib, config, ... }: with lib; let cfg = config.modules.system.frigate; nginx = config.modules.system.nginx; domain = "ramos.codes"; in { options.modules.system.frigate = { enable = mkEnableOption "Enable Frigate NVR"; }; config = mkIf cfg.enable { # go2rtc service (required - NixOS frigate doesn't bundle it) services.go2rtc = { enable = true; settings = { rtsp.listen = ":8554"; webrtc.listen = ":8555"; streams = { doorbell = "rtsp://admin:ocu%3Fu3Su@192.168.1.167/cam/realmonitor?channel=1&subtype=0#backchannel=1"; doorbell_sub = "rtsp://admin:ocu%3Fu3Su@192.168.1.167/cam/realmonitor?channel=1&subtype=1"; living_room = "rtsp://admin:ocu%3Fu3Su@192.168.1.147/cam/realmonitor?channel=1&subtype=0#backchannel=1"; living_room_sub = "rtsp://admin:ocu%3Fu3Su@192.168.1.147/cam/realmonitor?channel=1&subtype=1"; kitchen = "rtsp://admin:ocu%3Fu3Su@192.168.1.147/cam/realmonitor?channel=2&subtype=0#backchannel=1"; kitchen_sub = "rtsp://admin:ocu%3Fu3Su@192.168.1.147/cam/realmonitor?channel=2&subtype=1"; parking_lot = "rtsp://admin:ocu%3Fu3Su@192.168.1.194/cam/realmonitor?channel=1&subtype=0"; parking_lot_sub = "rtsp://admin:ocu%3Fu3Su@192.168.1.194/cam/realmonitor?channel=1&subtype=1"; }; }; }; services.frigate = { enable = true; hostname = "frigate.${domain}"; vaapiDriver = "i965"; # Haswell iGPU for H.264 decode settings = { mqtt.enabled = false; ffmpeg = { hwaccel_args = "preset-vaapi"; # VAAPI for H.264 substream detection input_args = "preset-rtsp-restream"; # TCP transport for go2rtc }; record = { enabled = true; # 24/7 recording - needs better hardware # retain = { # days = 14; # mode = "all"; # }; }; cameras = { doorbell = { enabled = true; detect = { enabled = true; width = 640; height = 480; }; ffmpeg.inputs = [ { path = "rtsp://127.0.0.1:8554/doorbell"; roles = [ "record" ]; } { path = "rtsp://127.0.0.1:8554/doorbell_sub"; roles = [ "detect" ]; } ]; }; living_room = { enabled = true; detect.enabled = false; # No motion detection for indoor audio = { enabled = true; listen = [ "bark" "fire_alarm" "scream" "speech" "yell" ]; }; ffmpeg.inputs = [ { path = "rtsp://127.0.0.1:8554/living_room"; roles = [ "record" "audio" ]; } ]; }; kitchen = { enabled = true; detect.enabled = false; # No motion detection for indoor audio = { enabled = true; listen = [ "bark" "fire_alarm" "scream" "speech" "yell" ]; }; ffmpeg.inputs = [ { path = "rtsp://127.0.0.1:8554/kitchen"; roles = [ "record" "audio" ]; } ]; }; parking_lot = { enabled = true; detect = { enabled = true; width = 640; height = 480; }; ffmpeg.inputs = [ { path = "rtsp://127.0.0.1:8554/parking_lot"; roles = [ "record" ]; } { path = "rtsp://127.0.0.1:8554/parking_lot_sub"; roles = [ "detect" ]; } ]; }; }; }; }; # Add SSL to frigate's nginx virtualHost services.nginx.virtualHosts."frigate.${domain}" = mkIf nginx.enable { useACMEHost = domain; forceSSL = true; locations."/go2rtc/" = { proxyPass = "http://127.0.0.1:1984/"; proxyWebsockets = true; }; }; # Frigate segment cache in RAM (reduces disk writes) fileSystems."/var/cache/frigate" = { device = "tmpfs"; fsType = "tmpfs"; options = [ "size=512M" "mode=0755" ]; }; systemd.tmpfiles.rules = [ # Set ownership after tmpfs mount "d /var/cache/frigate 0750 frigate frigate -" ]; # Backup recordings/database modules.system.backup = { paths = [ "/var/lib/frigate" ]; }; }; }