Encourage FUNIX

The consultation of the site is completely free and without advertisements. Donations are nevertheless appreciated to pay for hosting and encourage its author


NVR systems - Frigate overview

NVR systems

Presentation of Frigate and Frigate+

April 14, 2026


Presentation

This section aims to introduce you to various tools for setting up a Linux-based video surveillance system using independent surveillance cameras (IP or Wi-Fi), analog, digital, or simple webcams, also known as CCTV. ZoneMinder and Frigate are presented here. They all allow you to integrate a wide range of cameras, perform automatic motion detection, and create events. They are also web-based, allowing you to configure them, view the cameras in real time, and manage events via a browser, whether on a desktop or mobile device. ZoneMinder and Frigate are advanced tools with  machine learning and object recognition capabilities. ZoneMinder can also be considered more complex than Frigate, which integrates with Home Assistant.
This section is divided into several pages:

Personally, I use Frigate with the Frigate+ option, with a system architecture similar to the diagram below. Frigate runs on a dedicated mini PC accessible from the internet with an encrypted connection via a Let's Encrypt certificate. Everything is managed by Traefik, which also handles authentication. Both run on the Docker containerization tool. Locally, this mini PC is called Cerbere. It's supported by a secondary machine called Ultra, which manages a low-cost camera whose RTSP address fluctuates regularly. Ultra also runs an Apache httpd server. Cerbere uses a Google Coral TPU key to accelerate the training models. A total of six IP cameras are connected, distributed outdoors. Four are connected via PoE (Power over Ethernet), and three use PTZ (Pitch-to-Zoom) functionality and are mobile. The cameras include a video intercom controlled from a monitor connected indoors via PoE Ethernet. There's also a low-cost camera connected via a Wi-Fi repeater located in an outdoor electrically powered area.
Physically, my system is distributed across several zones:

  • Exterior: Location of the cameras, all of which withstand harsh weather and the southern sun very well.
  • Garage: Location of my Cerbere server and all the network connections in the house, with a standard switch to which the RJ45 cables serving the different rooms of the house are connected (and which replaced the telephone cables), and a PoE switch for the cameras.
  • Mezzanine: Location of my modem, router, and other Ultra server.
  • Outdoor area: Location of a Wi-Fi repeater to extend the range of a camera.
  • Kitchen: Location of the video intercom screen, whose camera is integrated into the system.

There are a plethora of IP cameras on the market; the cheapest are often Chinese models linked to an Android app with highly questionable security. The selection criteria are:

  • Cameras that generate an RTSP video stream with a static IP address and integrate very easily with ZoneMinder or Frigate. Websites like ipcamlive or ispyconnect list cameras by brand with a static RTSP address. Despite this, since I hadn't initially considered this criterion, I have one camera whose RTSP address changes regularly. It's not insurmountable, but it complicates the installation because scripts will need to be implemented to make the address static for ZoneMinder or Frigate.
  • Cameras can be configured by connecting via their IP address in a browser without needing an application. For the low-cost cameras, however, I had to use the Android app to configure them.
  • Administrative settings allow you to change network settings to connect to a local network. The cameras should not be directly accessible on the internet but only via Frigate or ZoneMinder.
  • It's not strictly mandatory, but it's better if the camera can connect via PoE Ethernet. This allows for a single cable (no power cable, everything goes through the Ethernet cable), over long distances (my longest cable is 30m) with a reliable and good quality connection. However, I have two low-cost cameras that are not PoE and are powered by a power cable in addition to being connected via standard Ethernet. One of them is more than 50 meters away and is connected to a Wi-Fi repeater. Choosing the right one was tricky because I needed a model that was fully configurable for network settings.

There's another component that gave me a lot of trouble: the Shorewall firewall. It needs to be both highly restrictive, blocking all direct internet access to the cameras, and capable of opening routes only when necessary so that Frigate can be securely accessed from a mobile device via Traefik. Traefik provides an encrypted connection with a Let's Encrypt certificate and can communicate with the Frigate+ databases. And I must admit, I wouldn't have managed it without Gemini's help—at least not so quickly.


Frigate Installation

Frigate relies on Docker, which I installed simply by typing on my Mageia.

urpmi docker docker-compose

Next, we launch Docker by typing

systemctl start docker

that is its status

systemctl status docker

and the result

docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; preset: disabled)
     Active: active (running) since Fri 2025-09-26 17:02:55 CEST; 6s ago
       Docs: http://docs.docker.com
    Process: 210883 ExecStartPre=/usr/sbin/docker-network-cleanup (code=exited, status=0/SUCCESS)
   Main PID: 210886 (dockerd)
      Tasks: 17
     Memory: 41.1M
        CPU: 369ms
     CGroup: /system.slice/docker.service
             ├─210886 /usr/sbin/dockerd --data-root /var/cache/docker -H unix:///var/run/docker.sock -H tcp://127.0.0.1:2375
             └─210899 containerd --config /var/run/docker/containerd/containerd.toml

sept. 26 17:02:52 ultra.kervao.fr dockerd[210899]: time="2025-09-26T17:02:52.687489959+02:00" level=info msg=serving... address=/var/run/docker/containerd/containerd.sock
sept. 26 17:02:52 ultra.kervao.fr dockerd[210899]: time="2025-09-26T17:02:52.687576078+02:00" level=info msg="containerd successfully booted in 0.201550s"
sept. 26 17:02:54 ultra.kervao.fr dockerd[210886]: time="2025-09-26T17:02:54.283134739+02:00" level=info msg="Loading containers: start."
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]: time="2025-09-26T17:02:55.007460149+02:00" level=info msg="Loading containers: done."
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]: time="2025-09-26T17:02:55.281188368+02:00" level=warning msg="WARNING: API is accessible on http://127.0.0.1:2375 without encryption>
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]: time="2025-09-26T17:02:55.281244407+02:00" level=info msg="Docker daemon" commit=library-import containerd-snapshotter=false storage>
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]: time="2025-09-26T17:02:55.281378785+02:00" level=info msg="Daemon has completed initialization"
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]: time="2025-09-26T17:02:55.804448712+02:00" level=info msg="API listen on /var/run/docker.sock"
sept. 26 17:02:55 ultra.kervao.fr systemd[1]: Started docker.service.
sept. 26 17:02:55 ultra.kervao.fr dockerd[210886]: time="2025-09-26T17:02:55.804558097+02:00" level=info msg="API listen on 127.0.0.1:2375"

to see if Docker is working correctly, we will type

Docker Run Hello World

Here is the result

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
17eec7bbc9d7: Pull complete
Digest: sha256:54e66cc1dd1fcb1c3c58bd8017914dbed8701e2d8c74d9262e26bd9cc1642d31
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

To have Docker launch on every reboot, type

systemctl enable docker

We will create the /etc/frigate and /etc/frigate/config directories ; the records will be stored in the /media/frigate directory. We return to /etc/frigate/config where I created the docker-compose.yml file which will contain

services:
  frigate:
    container_name: frigate
    privileged: true # this may not be necessary for all setups
    network_mode: host # j'ai dû rajouter cette ligne pour accéder à ma caméra restreamée avec mediamtx
    restart: unless-stopped

    stop_grace_period: 30s # allow enough time to shut down the various services
    image: ghcr.io/blakeblackshear/frigate:stable
    shm_size: "512mb" # update for your cameras based on calculation above
    devices:
    
- /dev/bus/usb:/dev/bus/usb # ligne pour l'accélérateur USB Google Coral, à commenter sinon
     - /dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel, needs to be updated for your hardware

    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/frigate/config:/config
      - /media/frigate:/media/frigate
      - type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports: #section inutile avec le mode réseau host
      - "8971:8971" # port de l'interface navigateur
      #- "8554:8554" # RTSP feeds, j'ai mis en commentaire car conflit avec mediamtx, utile pour renvoyer le flux vidéo vers homeassistant
      - "8555:8555/tcp" # WebRTC over tcp
      - "8555:8555/udp" # WebRTC over udp

[ back to top of page ]

and config.yaml in which we will define the cameras and objects to detect (list here https://docs.frigate.video/configuration/objects )

mqtt:
  enabled: false
cameras:
  Camera-sud:
    ffmpeg:
      inputs:
        - path: rtsp://frigate:passwd@192.168.2.110
          roles:
            - detect
            - record
      hwaccel_args: preset-vaapi
    objects:
      track:
        - person
        - dog
        - cat
        - bird
 
       - bicycle
        - car
        - motorcycle
    detect:
      width: 1280
      height: 720
    record:
      enabled: true
      retain:
        days: 7
        mode: all
  Camera-entree:
    ffmpeg:
      inputs:
        - path: rtsp://frigate:passwd@192.168.2.112
          roles:
            - detect
            - record
      hwaccel_args: preset-vaapi
    objects:
      track:
        - person
        - dog
        - cat
        - bird
 
       - bicycle
        - car
        - motorcycle
    detect:
      width: 1280
      height: 720
    record:
      enabled: true
      retain:
        days: 7
        mode: all
  Camera-piscine:
    ffmpeg:
      inputs:
        - path: rtsp://frigate:passwd@192.168.2.200:554/ch0_1.264
          roles:
            - detect
            - record
    objects:
      track:
        - person
        - dog
        - cat
        - bird

    detect:
      width: 800
      height: 448
    record:
      enabled: true
      retain:
        days: 7
        mode: all
  Camera-ouest:
    ffmpeg:
      inputs:
        - path: rtsp://ultra.kervao.fr:8554/cam1
          roles:
            - detect
            - record
    objects:
      track:

        - person
        - dog
        - cat
        - bird

    detect:
      width: 640
      height: 360
    record:
      enabled: true
      retain:
        days: 7
        mode: all
detectors:
  cpu1:
    type: cpu
    num_threads: 3
detect:
  enabled: true
version: 0.16-0

If you have a Google Coral USB accelerator, we will replace it with the detectors.

 coral:
    type: edgetpu
    device: usb

Also note that if you have an Intel CPU or GPU, for the hwaccel_args argument you have the following options:
  • preset-vaapi
  • preset-intel-qsv-h264 for H264 streams
  • preset-intel-qsv-h265 for H265 streams
The last two presets work on the latest processors, so they are likely more efficient.

Then type in this same directory

docker compose -f docker-compose.yml up -d

Here is the result: 9.6s

+] Running 10/10
 ✔ frigate 9 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                                                                                           79.5s
   ✔ b1badc6e5066 Pull complete                                                                                                                                                   1.2s
   ✔ 4f4fb700ef54 Pull complete                                                                                                                                                   0.3s
   ✔ 14d4601b76c8 Pull complete                                                                                                                                                  14.6s
   ✔ cab46c26a5aa Pull complete                                                                                                                                                   1.1s
   ✔ 6891a7512d41 Pull complete                                                                                                                                                  14.3s
   ✔ b44b002328e4 Pull complete                                                                                                                                                   8.3s
   ✔ 8074b201d282 Pull complete                                                                                                                                                   8.6s
   ✔ d823104cefbd Pull complete                                                                                                                                                   9.0s
   ✔ f86078113133 Pull complete                                                                                                                                                   9.6s
[+] Running 2/2
 ✔ Network config_default  Created                                                                                                                                                0.1s
 ✔ Container frigate       Started

some useful commands, restart frigate

docker restart frigate

stopper frigate

docker stop frigate 
[ back to top of page ]

First launch

Frigate is now installed, launched, and accessible locally from the browser by typing

https://ultra.kervao.fr:8971/

To find out the password you will need to type

docker logs frigate

and we see

2025-09-26 17:46:21.528704726  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************
2025-09-26 17:46:21.529867665  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************
2025-09-26 17:46:21.530590965  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    Auth is enabled, but no users exist.          ***
2025-09-26 17:46:21.531237207  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    Created a default user:                       ***
2025-09-26 17:46:21.542212889  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    User: admin                                   ***
2025-09-26 17:46:21.566794613  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    Password: 2bf8aeb98g16b66f54e70a8e8f121076   ***
2025-09-26 17:46:21.577589942  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************
2025-09-26 17:46:21.598394530  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************

With a TPU, we should get a message that looks like this.

2025-10-30 07:55:40.442764361  [2025-10-30 07:55:40] frigate.detectors.plugins.edgetpu_tfl INFO    : TPU found
2025-10-30 07:55:40.443899970  INFO: Created TensorFlow Lite XNNPACK delegate for CPU.

This allows you to log in, and here's the general interface with the latest events at the top and the live views just below.

With only the TPU, it's practically unusable on a daily basis; the program constantly uses almost 100% of the resources.

And with a TPU, it's like night and day!!

[ back to top of page ]

Configuration

Before going any further, it is important to understand some basic concepts of how Frigate works. We talk about alerts and detections. Alerts are based on detections but are considered priority events to be reported, while detections are less significant events that do not warrant notification but are still recorded and visible in the event review (see the documentation in English here https://docs.frigate.video/configuration/review/).

Let's move on to the settings, accessible via the gear icon in the bottom left corner of the main screen. This is the interface you get.


In the first tab, User Interface, there are general settings that I have not modified.

You can then access the camera settings via Camera Settings ; the Management menu allows you to activate or deactivate the cameras, which is useful when a camera is causing problems and to avoid error messages.

Before going to Activities, we'll start by defining the masks and detection zones via Masks/Zones. For each camera selectable from the blue button in the upper right corner, we declare detection zones; other areas of the image will be ignored for detection.



Note that you can select the type of object that can be detected and estimate the speed of the movement.




On the other hand, if we want only people and cars to be considered as alerts, we will need to modify the configuration file for the camera in question in the review section by adding a sub-section labels like this

    review:
      alerts:
        labels:
          - person
          - car
        required_zones: Zone-det-camera-sud
      detections:
        required_zones: Zone-det-camera-sud

We can also declare motion masks to prevent detection in places where there is a risk of unwanted movement (vegetation), which can disrupt object tracking.



To prevent your car, usually parked in its spot, from appearing as a false positive, you will need to create a specific object mask like this:



We now return to the Activities menu ; in the configuration below, only Person and Car objects will be displayed as alerts in the predefined area, and other detected objects will be classified as detections in the same area.

And so on for all cameras. Note that you will need to restart Frigate for the changes to take effect; this can be done from the Settings menu.

In the Enhancements menu, you can activate (they are disabled by default)

  • semantic search allows you to find tracked objects in your event review using either the image itself, a user-defined text description, or an automatically generated description.
  • facial recognition
  • license plate recognition
  • bird identification

The Users menu, as its name suggests, allows you to manage users. Two types of users are possible: administrators have access to all interface features, while observers are limited to viewing cameras, reviewing events, and viewing recording history within the user interface. The Notifications tab allows you to send email alerts.

Regarding recordings, by default we have

    record:
      enabled: true
      retain:
        days: 7
        mode: all

This means that both alerts and detections are recorded and stored for 7 days. For more detailed recording management, see this page: https://docs.frigate.video/configuration/record/

[ back to top of page ]

Advanced configuration

Adjust object detection

According to the page https://docs.frigate.video/frigate/camera_setup, the ideal resolution for an object to be detected should fit within a 320x320 pixel frame, which corresponds to the model used by Frigate. If the area of ​​motion is larger than 320x320, it will crop and resize it if necessary, potentially losing some detail in the process. Therefore, higher resolutions do not significantly improve detection accuracy; however, they do improve performance if the objects are very small in the image.
This is all rather counterintuitive because when I place a 320x320 frame on the camera image, which has a resolution of 1280x720, I think I'm unlikely to detect people at that scale with this camera, whereas a higher resolution could allow the detection of smaller objects.



By setting the maximum resolution of my camera to 3072x1728, this is what a 320x320 pixel box corresponds to; it's not a panacea for detection, but it's already better.


From this, we can derive the following rule: for a camera with a close field of view (an indoor camera, for example), a low resolution will suffice, while for a camera with a distant field of view (primarily an outdoor camera), a high resolution is preferable for better object detection.
I encourage you to place a 320x320 pixel frame over each of your scaled images to see if it's necessary to increase the camera resolution corresponding to the perspective of the objects you wish to detect.

Now, in case of a false positive, it will likely be necessary to adjust some settings; for details, please consult this page : https://docs.frigate.video/configuration/object_filters. In fact, several parameters must be considered for each object:
  • score : this is a non-modifiable parameter calculated for each image for each detected object
  • min_score : all detected objects with a score lower than this parameter will be ignored.
  • Threshold : This is the average of the scores for an object detected over several consecutive images.

Frigate calculates a score for each object detected in every image, and the object is considered positive when both the score exceeds the min_score and the threshold, which is the average of the scores over several consecutive images. This last parameter confirms the detection.
Here's what can be added to the config.yaml file as a common section for all cameras; these are values ​​commonly shared by the Frigate user community.

objects:
  filters:
    dog:
      min_score: .7
      threshold: .9
    cat:
      min_score: .65
      threshold: .8
    person:
      min_score: .7
      threshold: .9
    car:
      min_score: .65
      threshold: .85

These values ​​should not be considered ideal. Personally, with outdoor cameras that tend to have a wider field of view, I preferred to add a filters subsection to each objects section for each of my cameras to adjust these values ​​according to the camera, like this:

    objects:
      track:
        - person
        - dog
        - cat
        - face
      filters:
        person:
          min_score: .7
          threshold: .8

To further reduce false positives, a minimum and maximum detection zone can be defined; that is, if the object is too large or too small, it will be automatically rejected. For a stream with a resolution of 3072x1728, an image can be extracted at that size and opened in GIMP.


Using the selection tool, draw the min and max boxes to detect a car (for example). The width and height appear at the bottom center Rectangle: 245x252

The minimum detection box parameter is min_area and has a value of width*height, so for the example above, min_area=245x252=61470. The same applies to the max_area parameter. In practice, I set for a car

min_area 22000
max_area 230000

for a person

min_area 6000
max_area 30000

which gives us the following in config.yaml

filters:
        car:
          min_score: .7
          threshold: .9
          min_area: 22000
          max_area: 230000
        person:
          min_score: .7
          threshold: .8
          min_area: 6000
          max_area: 3000

Enable face detection

To enable face detection, we will first add the corresponding section to the config.yaml file.

face_recognition:
  enabled: true

And for the cameras in question, we will add the mention face in the track subsection.

objects:
      track:
        - person
        - dog
        - cat
        - bicycle
        - car
        - motorcycle
        - face

Frigate is restarted and the logs return the following messages

2025-11-02 11:20:57.493001613  [2025-11-02 11:20:57] frigate.config.config          WARNING : Camera-entree is configured to track ['face'] objects, which are not supported by the current model.
2025-11-02 11:20:57.495025758  [2025-11-02 11:20:57] frigate.config.config          WARNING : Camera-piscine is configured to track ['face'] objects, which are not supported by the current model.
2025-11-02 11:20:57.519957917  [2025-11-02 11:20:57] frigate.app                    INFO    : Starting Frigate (0.16.1-e664cb2)
2025-11-02 11:20:57.520656637  [2025-11-02 11:20:57] frigate.app                    INFO    : Creating directory: /media/frigate/clips/faces

(...)

2025-11-02 11:20:58.822822712  [2025-11-02 11:20:58] frigate.api.fastapi_app        INFO    : FastAPI started
2025-11-02 11:21:00.117012864  [2025-11-02 11:21:00] frigate.util.downloader        INFO    : Downloading complete: https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/facenet.tflite
2025-11-02 11:21:00.269584095  [2025-11-02 11:21:00] frigate.detectors.plugins.edgetpu_tfl INFO    : TPU found
2025-11-02 11:21:00.270930420  INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
2025-11-02 11:21:03.456767865  [2025-11-02 11:21:03] frigate.util.downloader        INFO    : Downloading complete: https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/landmarkdet.yaml

A Face Library icon appears



We add familiar faces by clicking on Add a face ; I selected two easily identifiable front-facing images to start with, one at a time.










Next, click the Train button in the top left corner and upload images of the person in different situations.







It adds the photo by zooming in on the face if it has been clearly identified. It is recommended to add a total of 20 to 30 images for maximum reliability. This is a bit tedious to do because it has to be done image by image. On my system, the images will be placed under /media/frigate/clips/faces.
And here's what it will look like when it identifies people.



Now you can train the models by clicking on the icon , you can confirm the detection by clicking on the people that were created previously.




However, if Frigate makes a mistake by displaying a false positive, you can delete them by selecting them using the CTRL command. Then click the Delete faces button in the top right corner.



More information here https://docs.frigate.video/configuration/face_recognition .

Implementing an object classification system

This feature, introduced in version 0.17.0, allows you to create subcategories of objects. In the example below, I'll differentiate between the cats my cameras usually see. In the main window, click the Classification icon.


We will create a subcategory of an existing object, here resident cats, distinguishing between cats



Next, you will need to select the images that correspond to Plumette the cat and the same for the other cat.



Next, we start learning the model




Here's what it looks like in the traces for a car class

2026-03-15 13:53:54.922614420  [2026-03-15 13:53:54] frigate.util.classification    INFO    : Wrote training metadata for Voitures: 32 images
2026-03-15 13:53:54.922912195  [2026-03-15 13:53:54] frigate.util.classification    INFO    : Finished training Voitures
2026-03-15 13:53:55.004893124  [2026-03-15 13:53:54] frigate.data_processing.real_time.custom_classification INFO    : Successfully loaded updated model for Voitures
2026-03-15 13:53:55.028924711  192.168.2.14 - - [15/Mar/2026:13:53:55 +0100] "GET /api/classification/Voitures/dataset HTTP/1.1" 200 1399 "http://192.168.2.11:8971/classification" "Mozilla/
5.0 (X11; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0" "-" request_time="0.004" upstream_response_time="0.003"

From there the objects will be identified by the subcategories thus created.

More information here: https://docs.frigate.video/configuration/custom_classification/object_classification




Establish a state classification system

This feature, introduced in version 0.17.0, allows you to distinguish the state of a particular object, such as whether a door or gate is open. In this example, I will consider the gate's open or closed state. In the main window, select the Classification icon.




In my example, the object to consider is the gate and its open or closed state.




We choose the camera and the area to monitor to check the gate's opening status



Click on Continue, then in normal use click again on Classification then States and there you can see the images of the portal, select them to indicate the state




We could potentially create a new state; for the portal, I created In Motion


You can then train the model by clicking on Train model in the upper right corner .

More information is available here: https://docs.frigate.video/configuration/custom_classification/state_classification


Connecting an ONVIF-compatible PTZ camera

PTZ cameras can also be animated using the ONVIF protocol. OnvifDeviceManager, presented here, can be used to determine how to access the camera's ONVIF settings. In the config.yaml file for the camera in question, add an onvif section after the ffmpeg section, like this:

Camera-est:
    ffmpeg:
      inputs:
        - path: rtsp://user:motdepasse@192.168.2.187:554/11
          roles:
            - detect
            - record
      hwaccel_args: []
  onvif:
      host: 192.168.2.187
      port: 8080
      user: "user"
      password: "motdepasse"

Restart Frigate, and the commands will appear in the interface.



in the logs, for each command this gives

2025-12-14 16:23:19.863412328  [2025-12-14 16:23:19] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_down for Camera-est
2025-12-14 16:23:20.014362034  [2025-12-14 16:23:20] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:24.154372463  [2025-12-14 16:23:24] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:24.324886496  [2025-12-14 16:23:24] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:27.660911995  [2025-12-14 16:23:27] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:27.831550797  [2025-12-14 16:23:27] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:31.340495182  [2025-12-14 16:23:31] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:31.471007427  [2025-12-14 16:23:31] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:35.053131483  [2025-12-14 16:23:35] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:35.203476745  [2025-12-14 16:23:35] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:38.560508556  [2025-12-14 16:23:38] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:38.691835827  [2025-12-14 16:23:38] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est




Add a Let's Encrypt certificat

Using a Apache httpd server

By default, Frigate generates a self-signed certificate to use port 8971, which triggers a security warning in the browser. In Firefox , you can accept the risk to access it. But this isn't ideal, especially if you're accessing Frigate from the internet. In the configuration presented here, we'll use Apache, not Nginx as is often suggested. For Let's Encrypt configuration with Apache, see this page.
The principle is as follows:

Internet -> HTTPS port 443 -> reverse proxy -> internal HTTP -> Frigate port 8971

Apache acts as a reverse proxy, meaning it allows access to an internal URL by automatically modifying the URL and pointing to the correct port. My internet-accessible address is https://adresseperso.ddns.net, which already points to an index.html page . This configuration therefore allows access to Frigate at https://adresseperso.ddns.net/frigate

In the Apache configuration file httpd.conf , the following modules will be enabled:

LoadModule proxy_module modules/mod_proxy.so 
LoadModule proxy_http_module modules/mod_proxy_http.so

and we will add the virtual host

<VirtualHost 192.168.2.13:443>
   ServerName adresseperso.ddns.net

   SSLEngine on
   SSLCertificateFile /etc/letsencrypt/live/adresseperso.ddns.net/fullchain.pem
   SSLCertificateKeyFile /etc/letsencrypt/live/adresseperso.ddns.net/privkey.pem

   ProxyPreserveHost On
   ProxyRequests Off

   # Redirection vers Frigate sur port 8971
   ProxyPass /frigate http://127.0.0.1:8971/frigate
   ProxyPassReverse /frigate http://127.0.0.1:8971/frigate

   # Websocket support
   RewriteEngine On
   RewriteCond %{HTTP:Upgrade} =websocket [NC]
   RewriteRule ^/frigate/(.*) ws://127.0.0.1:8971/frigate/$1 [P,L]
</VirtualHost>

We restart Apache by typing `systemctl restart httpd`

In the docker-compose.ym file , we will modify the default path in the environment section by adding

environment:
     FRIGATE_BASE_PATH: "/frigate"

To apply the changes, stop Frigate by typing docker stop frigate, then run

docker compose -f docker-compose.yml up -d

and restart Frigate by typing

docker start frigate

Using Traefik

We'll use Traefik as a reverse proxy ; it will manage the Let's Encrypt certificate and redirect secure traffic to Frigate . Another configuration detail: in my setup, I have two servers.

- Apache server, local address 192.168.2.13, accessible via myapacheserver.ddns.net
- Frigate server, local address 192.168.2.11, accessible via myfrigateserver.ddns.net

Traefik will be hosted on the Frigate server and will also manage the Let's Encrypt certificate and the redirection of secure traffic to the Apache server. On the modem/router, you need to open a route to the Traefik server with the local address 192.168.2.11, using ports 80 (for automatic certificate renewal) and 443. You will also need to open the same ports using Shorewall on the Traefik machine .
It is not necessary to open a route on the router to port 8971 (for login/password connections to Frigate) or port 5000 (for login/password connections to Frigate).

Centralization: Traefik becomes the sole entry point. Ports 80 and 443 are only opened on the PC running Docker/Traefik.
SSL for all:
The Apache httpd server will also automatically benefit from the secure HTTPS (SSL) connection, even if it doesn't manage it itself. Traefik handles decryption and redirects to the insecure HTTP connection to the Apache server on the local network.
Automatic renewal: Since port 80 points to Traefik, Let's Encrypt can renew the Frigate and Apache certificates without any intervention.

In summary, the principle for relaying to Apache is as follows.

Internet -> HTTPS port 443 -> reverse proxy -> HTTP interne -> Apache port 80

For Frigate, it's a little different because, since we connect to its port 8971 with an encrypted connection, Traefik will need to accept Frigate's self-signed certificate. This requires some additional configuration, particularly with the Shorewall firewall . The connection principle is as follows:

Internet -> HTTPS port 443 -> reverse proxy -> HTTPS interne -> Frigate port 8971

Regarding authentication, traefik will manage the login/password and transmit them to frigate without the need to re-enter them via the frigate login screen .

Let's start by creating a /etc/traefik directory, then navigate to it and create an empty acme.json file with 600 permissions; it will contain the certificates.

touch acme.json
chmod 600 acme.json

Then, we will create a traefik.yml file containing the following :

api:
dashboard: true # Active l'interface de contrôle sur le port 8080
insecure: true # Permet l'accès direct via le port 8080 sans SSL

# cela permet d'accepter le certicat autosigné de Frigate
serversTransport:
insecureSkipVerify: true

entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"

certificatesResolvers:
myresolver:
acme:
email: olivier.hoarau@funix.org
storage: acme.json
httpChallenge:
entryPoint: web

providers:
docker:
exposedByDefault: false # Sécurité : n'expose que les conteneurs avec le label 'enable=true'
file:
filename: dynamic_conf.yml # Ajoute la prise en charge du serveur Apache
watch: true

In the same place, we now create a dynamic_conf.yml file which will contain


http:
# 1. TRANSPORTS (Pour ignorer les erreurs de certificat auto-signé de Frigate)
serversTransports:
ignore-ssl:
insecureSkipVerify: true

# 2. ROUTERS (Configuration pour Apache)
routers:
to-apache:
rule: "Host(`monserveurapache.ddns.net`)"
service: apache-service
entryPoints:
- websecure
tls:
certresolver: myresolver

# 3. SERVICES (Cibles réseau pour apache)
services:
apache-service:
loadBalancer:
servers:
- url: "http://192.168.2.13:80"

# 4. MIDDLEWARES (Centralisation de l'auth pour Frigate)
middlewares:
frigate-auth:
basicAuth:
# liste des utilisateurs avec htpasswd
users:
- "frigateadmin:password-hash"
- "frigateuser:password-hash"

frigate-header:
headers:
customRequestHeaders:
# Traefik v3 utilise cette syntaxe pour injecter l'utilisateur authentifié
x-forwarded-user: '{{ printf "{{.RemoteUser}}" }}'
x-forwarded-groups: "admin"

We will create the password by typing

docker run --rm httpd:alpine htpasswd -nb frigateuser password

this should directly give something like frigateuser:password-hash and it will be enough to copy and paste it into the file above.The command below should give the same result :

echo $(/usr/local/apache2/bin/htpasswd -nB useradmin) | sed -e 's/\$/\$\$/g'

We will now create docker-compose.yml

services:
 traefik:
   image: traefik:v3.0
   command:
     - "--providers.docker=true"
     - "--serversTransport.insecureSkipVerify=true"
   container_name: traefik
   restart: always
   ports:
     - "80:80"
     - "443:443"
     - "8080:8080" # Dashboard (à protéger en production)
   volumes:
     - /var/run/docker.sock:/var/run/docker.sock:ro
     - ./traefik.yml:/traefik.yml:ro
     - ./dynamic_conf.yml:/dynamic_conf.yml:ro
     - ./acme.json:/acme.json
   networks:
     - proxy

networks:
 proxy:
   external: true

We now modify the Docker configuration file of /etc/frgate/config/docker-compose.yml frigate Here is the full text with the commented lines

services:
 frigate:
   container_name: frigate
   privileged: true # this may not be necessary for all setups
   #network_mode: host     
   restart: unless-stopped
   stop_grace_period: 30s # allow enough time to shut down the various services
   image: ghcr.io/blakeblackshear/frigate:0.17.0
   shm_size: "540mb" # update for your cameras based on calculation above
   devices:
     - /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions
     - /dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel, needs to be updated for your hardware
   volumes:
     - /etc/localtime:/etc/localtime:ro
     - /etc/frigate/config:/config
     - /media/frigate:/media/frigate
     - type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
       target: /tmp/cache
       tmpfs:
         size: 1000000000
   ports:
     - "8971:8971"
     #- "5000:5000" # Internal unauthenticated access. Expose carefully.
     - "8554:8554" # RTSP feeds
     - "8555:8555/tcp" # WebRTC over tcp
     - "8555:8555/udp" # WebRTC over udp
   environment:
     #- FRIGATE_BASE_PATH="/frigate"
     - PLUS_API_KEY=clé-frigate-plus
     - FRIGATE_AUTH_METHOD=proxy
     - FRIGATE_PROXY_HEADER=Remote-User
     - FRIGATE_TRUSTED_PROXIES=172.18.0.0/16
   networks:
     - proxy # Doit être sur le même réseau que Traefik
   labels:
     - "traefik.enable=true"
     - "traefik.http.routers.frigate.rule=Host(`monserveurfrigate.ddns.net`)"
     - "traefik.http.routers.frigate.entrypoints=websecure"
     - "traefik.http.routers.frigate.tls.certresolver=myresolver"

     # Appelle les middlewares définis dans le fichier (d'où le @file)
     - "traefik.http.routers.frigate.middlewares=frigate-auth@file,frigate-header@file"

     # Configuration du service vers le port 8971
     - "traefik.http.services.frigate-service.loadbalancer.server.port=8971"
     - "traefik.http.services.frigate-service.loadbalancer.server.scheme=https"

     # UTILISATION DU TRANSPORT DEFINI DANS LE FICHIER DYNAMIQUE
     - "traefik.http.services.frigate-service.loadbalancer.serverstransport=ignore-ssl@file"

networks:
 proxy:
   external: true


in the /etc/frigate/config/config.yaml file we will have

tls:
 enabled: true
auth:
 enabled: true
proxy:
 header_map:
   user: x-forwarded-user
   role: x-forwarded-groups

Before launching Traefik and restarting Frigate, you'll need to create the Docker network with the name proxy by typing

docker network create proxy

This will give you the proxy network's identification number, a long string of numbers and letters. I then simplified the Apache configuration by removing all VirtualHosts associated with my address monserveurapache.ddns.net and all automatic port forwarding from port 80 to 443. Since Traefik will now send requests to Apache via HTTP (port 80) on your local network, I've re-enabled it to listen on the default port 80.

Let's now move on to configuring shorewall under /etc/shorewall. First, in the shorewall.conf file, ensure you have

DOCKER=Yes

DOCKER_BRIDGE=docker0

IP_FORWARDING=Keep

ROUTE_FILTER=No

file Interfaces

#
# Shorewall -- /etc/shorewall/interfaces
#
# For information about entries in this file, type "man shorewall-interfaces"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-interfaces.html
#
?FORMAT 2 
loc     enp1s0  dhcp,nosmurfs,routefilter
# We use 'physical' to prevent Shorewall from search for the broadcast
dock     docker0          physical=docker+,routeback=1
dock     br               physical=br-+,routeback=1

file zones

#
# Shorewall -- /etc/shorewall/zones
#
# For information about this file, type "man shorewall-zones"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-zones.html
#
###############################################################################
#ZONE           TYPE            OPTIONS         IN_OPTIONS      OUT_OPTIONS
fw      firewall
dock    ipv4
net ipv4
ipv4 loc

file policy

#
# Shorewall -- /etc/shorewall/policy
#
# For information about entries in this file, type "man shorewall-policy"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-policy.html
#
###############################################################################
$FW        dock      ACCEPT
dock       $FW       ACCEPT
dock       dock      ACCEPT
dock       net       ACCEPT

fw      all     ACCEPT
loc     all     ACCEPT
all     all     REJECT

file snat

#
# Shorewall -- /etc/shorewall/snat
#
# For information about entries in this file, type "man shorewall-snat"
#
# See https://shorewall.org/manpages/shorewall-snat.html for more information
#
?FORMAT 2
###################################################################################################################################################
#ACTION                 SOURCE                  DEST            PROTO   DPORT   SPORT   IPSEC   MARK    USER    SWITCH  ORIGDEST        PROBABILITY
MASQUERADE  172.18.0.0/16       enp1s0
MASQUERADE  172.17.0.0/16       enp1s0

file rules

#
# Shorewall -- /etc/shorewall/rules
#
# For information on the settings in this file, type "man shorewall-rules"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-rules.html
#
##############################################################################################################################################################
#ACTION         SOURCE          DEST            PROTO   DPORT   SPORT   ORIGDEST        RATE    USER    MARK    CONNLIMIT       TIME    HEADERS SWITCH  HELPER
#INCLUDE        rules.drakx

# ==============================================================================
# 1. FLUX DOCKER (FRIGATE / TRAEFIK INTERNAL)
# ==============================================================================
# Communication entre l'hôte et les conteneurs (Port Frigate+)
ACCEPT    $FW                dock:172.18.0.0/16    tcp    8971
ACCEPT    dock:172.18.0.0/16 $FW                   tcp    8971

# Sortie DNS pour les conteneurs (indispensable pour Frigate+)
ACCEPT    dock:172.18.0.0/16 net                   udp    53
ACCEPT    dock:172.18.0.0/16 net                   tcp    53

# Sortie HTTPS pour les conteneurs (Upload photos Frigate+)
ACCEPT    dock:172.18.0.0/16 net                   tcp    443

# ==============================================================================
# 2. ACCÈS TRAEFIK (HTTP/HTTPS)
# ==============================================================================
# On autorise le Web depuis Internet ET depuis le réseau local
# Note : On utilise $FW car 192.168.2.11 est l'IP de cette machine.
ACCEPT    net                $FW                   tcp    80,443
ACCEPT    loc                $FW                   tcp    80,443

# Ports spécifiques (WebRTC / Go2RTC)
ACCEPT    net                $FW                   tcp    8555
ACCEPT    net                $FW                   udp    8555
ACCEPT    loc                $FW                   tcp    8555
ACCEPT    loc                $FW                   udp    8555

# Redirection du trafic Web vers le conteneur Traefik
DNAT    net    dock:172.18.0.2    tcp    80,443
DNAT    loc    dock:172.18.0.2    tcp    80,443

# ==============================================================================
# 3. ACCÈS LOCAL & ADMINISTRATION (SSH, MQTT, FRIGATE UI)
# ==============================================================================
# Autorise ton LAN à administrer le serveur
ACCEPT    loc                $FW                   tcp    22,1883,5000,8080,8971,8554
ACCEPT    loc                $FW                   udp    554,2049,8000
ACCEPT    loc                $FW                   icmp

# ==============================================================================
# 4. SORTIE SERVEUR (PING & MAJ)
# ==============================================================================
ACCEPT    $FW                net                   icmp
ACCEPT    $FW                net                   tcp    80,443
ACCEPT    $FW                net                   udp    53

# ==============================================================================
# 5. FLUX DOCKER VERS LAN (POUR APACHE EXTERNE)
# ==============================================================================
# Autorise Traefik (zone dock) à joindre Apache (zone loc)
ACCEPT    dock:172.18.0.0/16    loc:192.168.2.13    tcp    80

# ==============================================================================
# 6. FLUX FRIGATE -> CAMÉRAS (RÉSEAU LOCAL)
# ==============================================================================
# Autorise Frigate à lire les flux RTSP (vidéo) et HTTP (commandes/snapshot)
ACCEPT    dock:172.18.0.0/16    loc    tcp    554,80,81,8000,8554
ACCEPT    dock:172.18.0.0/16    loc    udp    554,8000-8010


we restarting shorewall

 
Now we're going to launch Traefik ; under /etc/traefik we will type

docker compose up -d

and we restart the frigate configuration by typing

docker stop frigate
cd /etc/frigate/config
docker compose down
docker compose up -d

To see if it works, we can type

docker network inspect proxy

here is the result

[
   {
       "Name": "proxy",
       "Id": "id-reseau-proxy",
       "Created": "2026-03-21T07:40:45.423464906+01:00",
       "Scope": "local",
       "Driver": "bridge",
       "EnableIPv4": true,
"EnableIPv6": false,
"IPAM": {
           "Driver": "default",
           "Options": {},
           "Config": [
               {
                   "Subnet": "172.18.0.0/16",
                   "Gateway": "172.18.0.1"
               }
           ]
       },
       "Internal": false,
"Attachable": false,
       "Ingress": false,
       "ConfigFrom": {
           "Network": ""
       },
       "ConfigOnly": false,
       "Containers": {
"1935c6946444a4be193c3fc3cf89ad4e00d76b0dace34c032266c72a36789caf": {
               "Name": "frigate",
               "EndpointID": "e3630a1c73245a57c88048edbb1c9e49dee86d925ff8a735c42827b1b4959823",
"MacAddress": "06:fa:01:8a:b1:5a",
               "IPv4Address": "172.18.0.2/16",
               "IPv6Address": ""
           },
           "564a20b201a1adf544020be253b267f5472c3cceec0cdc31f208b4f02089141e": {
"Name": "traffic",
               "EndpointID": "671f4968920ec3497c8bbfe8855c99e10626907159abc374bf5d0be8ea555bbd",
"MacAddress": "46:2e:a8:5b:bc:ec",
               "IPv4Address": "172.18.0.3/16",
               "IPv6Address": ""
           }
       },
       "Options": {},
       "Labels": {}
   }
]

instances are clearly visible. The Traefik and Frigate are indeed on the same proxy network.

You can now go to http://192.168.2.11:8080 to dashboard see the Traefik, we can check if the routes are active and the certificates are valid.




By clicking on Explore under Routers, you can see both the frigate and the apache httpd server, each with a valid certificate.



I've kept port 8080 open on the local network, but it's inaccessible from the internet.
If needed, view the acme.json file ; you should find your certificates there, and we can display the Traefik logs by typing

docker logs -f traefik

Perform an update

To perform an update, we start by stopping the frigate by typing

docker stop frigate

We now edit the docker-compose.yml file and specify the latest stable version in the image section

services:
 frigate:
  (...)
   image: ghcr.io/blakeblackshear/frigate:0.17.0

The modification is taken into account by typing

docker compose up -d

This is what it looks like

[+] Running 11/11
✔ frigate 10 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                                                                                              160.9s  
  ✔ 4831516dd0cb Pull complete                                                                                                                                                         1.9s  
  ✔ 4f4fb700ef54 Pull complete                                                                                                                                                         1.2s  
  ✔ cb7d423543ee Pull complete                                                                                                                                                        19.8s  
  ✔ d2f5da7ad830 Pull complete                                                                                                                                                         2.6s  
  ✔ 7e04514a5073 Pull complete                                                                                                                                                        32.6s  
  ✔ 34300798769b Pull complete                                                                                                                                                         5.7s  
  ✔ 2ad10e3dbd66 Pull complete                                                                                                                                                        18.0s  
  ✔ 848dd1cbc10e Pull complete                                                                                                                                                        18.5s  
  ✔ 8226412a03ca Pull complete                                                                                                                                                        19.0s  
  ✔ b22355f413a7 Pull complete                                                                                                                                                        19.8s  
[+] Running 2/2
✔ Container frigate                                                  Started                                                                                                          12.1s  
! frigate Published ports are discarded when using host network mode                                                                                                                   0.0s

and we can see in the logs that it is indeed taken into account by typing

docker logs frigate

we can see
 
2026-02-27 18:03:04.431938998  [2026-02-27 18:03:04] frigate.app                    INFO    : Starting Frigate (0.17.0-f0d69f7)

If you have the error

[2026-02-27 18:03:15] frigate.api.auth               ERROR   : Error parsing jwt: bad_signature:

You will need to stop Frigate and delete the file /etc/frigate/config/.jwt_secret and clear the cache of the browser that accesses Frigate and relaunch Frigate.

For the following error

2026-02-27 18:03:15.508446035  Fatal Python error: Illegal instruction
2026-02-27 18:03:15.508451526
2026-02-27 18:03:15.508494078  Thread 0x00007f2970ce66c0 (most recent call first):
2026-02-27 18:03:15.508587864    File "/usr/lib/python3.11/threading.py", line 320 in wait
2026-02-27 18:03:15.508683237    File "/usr/lib/python3.11/queue.py", line 171 in get
2026-02-27 18:03:15.508775665    File "/usr/local/lib/python3.11/dist-packages/playhouse/sqliteq.py", line 162 in loop
2026-02-27 18:03:15.508860665    File "/usr/local/lib/python3.11/dist-packages/playhouse/sqliteq.py", line 137 in run
2026-02-27 18:03:15.508965544    File "/usr/local/lib/python3.11/dist-packages/playhouse/sqliteq.py", line 272 in run
2026-02-27 18:03:15.509051339    File "/usr/lib/python3.11/threading.py", line 975 in run
2026-02-27 18:03:15.509143141    File "/usr/lib/python3.11/threading.py", line 1038 in _bootstrap_inner
2026-02-27 18:03:15.509234196    File "/usr/lib/python3.11/threading.py", line 995 in _bootstrap
2026-02-27 18:03:15.509249695
2026-02-27 18:03:15.509286238  Current thread 0x00007f299b08a040 (most recent call first):
2026-02-27 18:03:15.509368220    File "<frozen importlib._bootstrap>", line 241 in _call_with_frames_removed
2026-02-27 18:03:15.509501615    File "<frozen importlib._bootstrap_external>", line 1233 in create_module
2026-02-27 18:03:15.509584820    File "<frozen importlib._bootstrap>", line 573 in module_from_spec

(...)
 
2026-02-27 18:03:15.514956630    File "/usr/lib/python3.11/multiprocessing/spawn.py", line 133 in _main
2026-02-27 18:03:15.514958723    File "/usr/lib/python3.11/multiprocessing/forkserver.py", line 313 in _serve_one
2026-02-27 18:03:15.514960962    File "/usr/lib/python3.11/multiprocessing/forkserver.py", line 274 in main
2026-02-27 18:03:15.514962618    File "<string>", line 1 in <module>
                                                                                             
In Frigate 0.17, the new embedding features (semantic search, facial recognition, etc.) use more complex AI models via TensorFlow and Jina. These libraries are compiled for instructions modern processors supporting AVX/AVX2. To find out if your processor has these instructions you have to type

grep -o 'avx[^ ]*' /proc/cpuinfo

If nothing comes out, it means the processor is too slow. In the past, disabling semantic search was sufficient to resolve the problem in the config.yaml file.

[ back to top of page ]

Use

Classic use

Now let's return to the main interface. We saw earlier that the main view links to the camera views; it is accessible by clicking on the icon representing a camera on the left.

Clicking on the Event Review on the left displays all recent events (alerts, detections, movement). The detected object appears as an icon in the top left corner of the image; you can access the replay by clicking on the image. You can save the review by clicking on Mark these items as reviewed at the bottom.



When you click on the small icon in the top left corner that represents the detected object, you'll see the event details. On the right, there's a timeline with the day's most recent events. You can go even further by clicking on the image of the detected object; this opens a window that allows you to view the video with brief details about the detection.




When you click on the ... to the right of the detected object, you will see this menu to explore further.



To display the object's kinematics, click on Show object path in the image below. The trajectory may appear strange because the camera is PTZ and has automatically oriented itself towards the object to follow it.




The Explorer menu on the left allows you to display all detected objects, which can be filtered using the tools in the top right corner.



When you click on a detection image you can view the kinematics of the object in more detail with the detection parameters which can be used to refine the frigate configuration .


You can delete them one by one using the context menu, or in batches with CTRL-A and then the Delete button in the top right corner.

As we saw earlier , we could access the system resource status via the Settings->System->System Metrics menu ; there is also a Storage tab that provides a status of recordings



And the Cameras tab, which provides a status of the resources used by each camera, even if the TPU is not visible.


For the rest, I refer you to the Frigate documentation for usage instructions, which includes some useful URLs.

https://docs.frigate.video/guides/getting_started
https://community.jeedom.com/t/tuto-integrer-frigate-et-faire-de-la-reconnaissance-video-dobjet-par-ia-en-local-dans-son-jeedom/117108
https://www.simplehomelab.com/frigate-docker-guide/


View Frigate from a mobile device

Regarding remote viewing on a mobile device, there is indeed an unofficial Android application, but I don't really see the point because simply accessing the address directly (once it's made accessible on the internet) from a mobile browser is quite user-friendly, as you can see below.


Notifications with Signal via MQTT

Presentation

Frigate already integrates an email notification service, as mentioned above, which doesn't work for me, but regardless, this paragraph explains how to receive alert notifications with Signal using the MQTT service. MQTT (Message Queuing Telemetry Transport) is a messaging protocol that works on a subscription/publish basis, meaning that a server (or broker) can publish notifications on a channel, and clients can subscribe to view them. With Frigate, by enabling MQTT, you can run a script that triggers an action upon receiving an event, such as an alert. It's also possible to make the MQTT broker accessible from the internet, and an Android MQTT client application can be installed to receive alerts.

Mosquito Installation

The official website is https://mosquitto.org. On my Mageia system, I simply installed it by typing urpmi mosquitto. We created a user with their password:

mosquitto_passwd -c /etc/mosquitto/passwd olivier
Password:
Re-enter password:

in the file
/etc/mosquitto/mosquitto.conf we uncommented and modified the following two lines:

allow_anonymous false
password_file /etc/mosquitto/passwd


listener 1883 0.0.0.0


We changed the owner of the passwd file :

chown mosquitto:mosquitto /etc/mosquitto/passwd

We can then launch mosquitto with

systemctl start mosquitto

Here is the output of the command systemctl status mosquitto

mosquitto.service - Mosquitto MQTT Broker
    Loaded: loaded (/usr/lib/systemd/system/mosquitto.service; disabled; preset: disabled)
    Active: active (running) since Sun 2025-12-07 09:14:56 CET; 2min 12s ago
      Docs: man:mosquitto.conf(5)
            man:mosquitto(8)
   Process: 1426932 ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto (code=exited, status=0/SUCCESS)
   Process: 1426933 ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto (code=exited, status=0/SUCCESS)
   Process: 1426934 ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto (code=exited, status=0/SUCCESS)
   Process: 1426935 ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto (code=exited, status=0/SUCCESS)
  Main PID: 1426936 (mosquitto)
     Tasks: 1 (limit: 18901)
    Memory: 1004.0K
       CPU: 43ms
    CGroup: /system.slice/mosquitto.service
            └─1426936 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

déc. 07 09:14:56 ultra.kervao.fr systemd[1]: Starting mosquitto.service...
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: mosquitto version 2.0.21 starting
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Config loaded from /etc/mosquitto/mosquitto.conf.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Starting in local only mode. Connections will only be possible from clients running on this machine.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Create a configuration file which defines a listener to allow remote access.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: For more details see https://mosquitto.org/documentation/authentication-methods/
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Opening ipv4 listen socket on port 1883.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Opening ipv6 listen socket on port 1883.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: mosquitto version 2.0.21 running
déc. 07 09:14:56 ultra.kervao.fr systemd[1]: Started mosquitto.service.

Don't forget to open TCP port 1883 used by MQTT via Shorewall. To see if it works well, we can launch a client that will wait for a notification from a broker:

mosquitto_sub -v -t 'test/topic' -u olivier -P password

Now we will launch a broker by publishing a notification:

mosquitto_pub -u olivier -P password -t 'test/topic' -m 'helloWorld'


and from the client we will see
test/topic helloWorld displayed. Note that the broker runs on the server where Frigate is located, the whole thing runs locally and I don't need it to be accessible from the internet.




Frigate configuration and alert management scripts

Now, in the Frigate configuration file config.yaml, we'll enable MQTT :

mqtt:
 enabled: true
 host: localhost
 user: olivier
 password: motdepasse
 port: 1883

We'll create the listening script
/usr/local/bin/alerte_frigate which will send an email as soon as there's an alert. We'll see about Signal later.


#!/bin/bash
mosquitto_sub -t "frigate/events" -u olivier -P motdepasse | while read -r payload
do
   #à décommenter pour visualiser pour des tests de fonctionnemnet
   #echo "Rx MQTT: ${payload}"
   #envoi d'un mail pour chaque nouveau évènement
   if echo "${payload}" | grep -q '"type": "new"'; then
       echo "${payload}" | mail -s "Frigate alerte" olivier
   fi
done

The email will contain


{"before": {"id": "1765103169.376268-4ybyyv", "camera": "Camera-sud", "frame_time": 1765103169.376268, "snapshot": null, "label": "car", "sub_label": null, "top_score": 0.0, "false_positive": true, "start_time": 1765103169.376268, "end_time": null, "score": 0.9765625, "box": [1876, 1001, 2407, 1285], "area": 150804, "ratio": 1.869718309859155, "region": [1356, 0, 3072, 1716], "active": true, "stationary": false, "motionless_count": 0, "position_changes": 0, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": false, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null}, "after": {"id": "1765103169.376268-4ybyyv", "camera": "Camera-sud", "frame_time": 1765103169.59021, "snapshot": {"frame_time": 1765103169.376268, "box": [1876, 1001, 2407, 1285], "area": 150804, "region": [1356, 0, 3072, 1716], "score": 0.9765625, "attributes": [], "current_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null, "recognized_license_plate_score": null}, "label": "car", "sub_label": null, "top_score": 0.9765625, "false_positive": false, "start_time": 1765103169.376268, "end_time": null, "score": 0.9765625, "box": [1872, 1003, 2411, 1287], "area": 153076, "ratio": 1.897887323943662, "region": [1356, 0, 3072, 1716], "active": true, "stationary": false, "motionless_count": 1, "position_changes": 0, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": false, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [[[0.6969, 0.7448], 1765103169.59021]], "recognized_license_plate": null}, "type": "new"}

The script will filter only new alerts; however, it will handle both alerts and detections, as there are no fields to distinguish between them.

Here's a more advanced script (thanks ChatGPT !!) that will send an alert email only for person and car objects, attaching a snapshot with the outline around the object. It will require the installation of the jq package, which will allow for easier extraction of useful information.

#!/bin/bash
# /usr/local/bin/alerte_frigate
# Attendre que le clip soit complètement écrit (taille stable) avant envoi

#paramètres de connexion MQTT"
MQTT_USER="olivier"
MQTT_PASS="motdepasse"
MQTT_TOPIC="frigate/events"

#paramètre de connexion API FRIGATE

FRIGATE_USER="loginfrigate"
FRIGATE_PASS="motdepasse"
FRIGATE_API="https://192.168.2.11:8971"
COOKIE_FILE="/tmp/frigate_cookies.txt"

CLIP_DIR="/media/frigate/clips"

MAIL_FROM="webmaster@funix.org"
MAIL_TO="olivier.hoarau@funix.org"

THROTTLE_SECONDS=40        # délai minimum entre deux alertes par caméra
THROTTLE_DIR="/var/tmp/frigate_throttle"
mkdir -p "$THROTTLE_DIR"

SIGNAL_USER="+336XXXXXXXX"       # ton numéro Signal
SIGNAL_DEST="+336YYYYYYYY"       # numéro qui reçoit les alertes

LOGGER_TAG="alerte_frigate"

### --- LOG FUNCTION ---
log()     { logger -t "$LOGGER_TAG" "[INFO] $*"; }
log_warn(){ logger -t "$LOGGER_TAG" "[WARN] $*"; }
log_dbg() { logger -t "$LOGGER_TAG" "[DEBUG] $*"; }

log "Démarrage du service."

# Paramètres d'attente pour la stabilité du fichier
WAIT_MAX_SECS=20       # temps max d'attente pour que le fichier soit complet
CHECK_STEP=0.5         # intervalle entre deux vérifs de taille (sec)
STABLE_ROUNDS=2        # nombre de mesures identiques successives pour considérer "stable"

# Fonction qui permet une alerte max par caméra toutes les THROTTLE_SECONDS secondes
should_throttle() {
   local cam="$1"
   local now
   now=$(date +%s)
   local file="$THROTTLE_DIR/$cam.last"

   # Si jamais aucun envoi encore
   if [ ! -f "$file" ]; then
       echo "$now" > "$file"
       return 1    # PAS de throttle
   fi

   local last
   last=$(cat "$file")

   local delta=$((now - last))

   if [ "$delta" -lt "$THROTTLE_SECONDS" ]; then
       # throttle actif
       return 0
   fi

   # assez de temps écoulé, mise à jour
   echo "$now" > "$file"
   return 1
}

# Premier login au lancement du script
#login_frigate

mosquitto_sub -t "$MQTT_TOPIC" -u "$MQTT_USER" -P "$MQTT_PASS" | while read -r payload
do
   # Ne traiter que les "new"
   if echo "$payload" | jq -e . >/dev/null 2>&1; then
       if ! echo "$payload" | grep -q '"type"[[:space:]]*:[[:space:]]*"new"'; then
           continue
       fi
   else
       continue
   fi

   # Extraire infos
   camera=$(echo "$payload" | jq -r '.after.camera // .camera // empty')
   label=$(echo "$payload" | jq -r '.after.label // "inconnu"')
   id=$(echo "$payload" | jq -r '.after.id // .id // empty')

   # THROTTLE PAR CAMERA
   if should_throttle "${camera}"; then
       log_dbg "Throttle actif : alerte ignorée sur ${camera} (moins de $THROTTLE_SECONDS secondes)"
       continue
   fi

   # Filtrer uniquement les labels car et person
   if [[ "${label}" != "car" && "${label}" != "person" ]]; then
       log_dbg "Événement ignoré pour label=${label}"
       continue
   fi

   if [ -z "$id" ] || [ -z "${camera}" ]; then
       log_warn "Événement MQTT invalide, ignoré."
       continue
   fi

   EVENT_ID="$id"
   log "Détection : caméra=${camera}, label=${label}, id=${EVENT_ID}"
   log_dbg "Recherche du fichier annoté…"
    
   CLIP_FILE=""
   MAX_WAIT=20   # secondes
   WAIT=0  
   while [ $WAIT -lt $MAX_WAIT ]; do
       CLIP_FILE=$(find "$CLIP_DIR" -type f -iname "*${EVENT_ID}*.jpg" | head -n 1)

       if [ -n "$CLIP_FILE" ]; then
                   log_dbg "Fichier trouvé après ${WAIT}s : $CLIP_FILE"
               break
       fi

       sleep 1
       WAIT=$((WAIT+1))
   done

   if [ -z "$CLIP_FILE" ]; then
          log_warn "Aucun snapshot annoté trouvé pour $EVENT_ID après ${MAX_WAIT}s."
   else
          log "[INFO] Clip annoté trouvé:  $CLIP_FILE"
   fi

   ATTACH=""
   # Si clip trouvé, attendre la stabilisation de la taille
   if [[ -n "$CLIP_FILE" && -f "$CLIP_FILE" ]]; then
       log "Clip candidate trouvé: $CLIP_FILE"
       elapsed=0
       stable_count=0
       last_size=-1

       while (( $(echo "$elapsed < $WAIT_MAX_SECS" | bc -l) )); do
           if [[ -f "$CLIP_FILE" ]]; then
               size=$(stat -c %s "$CLIP_FILE" 2>/dev/null || echo 0)
           else
               size=0
           fi

           if [[ "$size" -eq "$last_size" && "$size" -gt 0 ]]; then
               stable_count=$((stable_count+1))
           else
               stable_count=0
           fi

           # Si on a STABLE_ROUNDS mesures identiques => on le considère complet
           if (( stable_count >= STABLE_ROUNDS )); then
               log "Fichier stable (taille=$size). Prêt à être envoyé."
               ATTACH="$CLIP_FILE"
               break
           fi

           last_size=$size
           sleep "$CHECK_STEP"
           elapsed=$(echo "$elapsed + $CHECK_STEP" | bc -l)
       done

       # si pas stable au bout du timeout, on considère quand même (mais on peut fallback)
       if [[ -z "$ATTACH" ]]; then
           log_warn "Fichier non stable après ${WAIT_MAX_SECS}s (taille actuelle=${last_size}). Tentative fallback API."
       fi
   else
       log_warn "Aucun clip annoté trouvé localement pour ${EVENT_ID}."
   fi

   # Si pas d'attach encore, tenter de récupérer la snapshot via API
   if [[ -z "$ATTACH" ]]; then
       TMPFILE="/tmp/frigate_snapshot_${EVENT_ID//[^a-zA-Z0-9._-]/_}.jpg"
       rm -f "$TMPFILE"

       # Récupération via API (retry court car la snapshot peut être générée juste après l'event)
       got=0
       retries=10
       sleep_between=0.5
       #connexion sécurisée à l'API FRIGATE en passant par la création d'un cookie
       curl -k -c ${COOKIE_FILE} -X POST -d "user=${FRIGATE_USER}&password=${FRIGATE_PASS}" "${FRIGATE_API}/api/login"
       for ((i=0;i<retries;i++)); do
           curl -k -c ${COOKIE_FILE} -s -f "${FRIGATE_API}/api/events/${EVENT_ID}/snapshot.jpg" -o "$TMPFILE"
           if [[ $? -eq 0 && -s "$TMPFILE" ]]; then
               log "Snapshot récupérée via API -> $TMPFILE"
               ATTACH="$TMPFILE"
               got=1
               break
           fi
           sleep "$sleep_between"
       done

       if [[ $got -ne 1 ]]; then
           log_warn "API snapshot échouée ou introuvable pour ${EVENT_ID}."
       fi
   fi
    
   ### --- MAIL HTML ---
   subject="Frigate : détection de ${label} sur ${camera}"
   body="Nouvelle détection Frigate.\n\nCaméra : ${camera}\nObjet  : ${label}\nID     : ${EVENT_ID}\n\nPayload brut :\n${payload}"

   if [[ -n "$ATTACH" && -f "$ATTACH" ]]; then
      echo -e "$body" | mail -s "$subject" -r "$MAIL_FROM" -a "$ATTACH" "$MAIL_TO"
      log "Mail envoyé avec pièce jointe : $ATTACH"
      # si tmp file, on peut le supprimer
      if [[ "$ATTACH" == /tmp/frigate_snapshot_* ]]; then
          rm -f "$ATTACH"
      fi
   else
      echo -e "$body" | mail -s "$subject" -r "$MAIL_FROM" "$MAIL_TO"
      log "Mail envoyé sans pièce jointe."
   fi
 
done
Okay, so we'll create a systemd service so the script runs automatically. Here's the content of /usr/lib/systemd/system/frigate_alert.service:

[Unit]
Description=Frigate MQTT Alert Email Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/alerte_frigate
Restart=always
RestartSec=5
User=root
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

[Install]
WantedBy=multi-user.target

It is activated by typing

systemctl enable frigate_alert

and then launched using the command


systemctl start frigate_alert

We're thinking of restarting Frigate because we've changed its configuration.

docker stop frigate
docker start frigate

Here's what journalctl returns :

déc. 07 12:20:43 ultra.kervao.fr systemd[1]: Started frigate_alert.service.
déc. 07 12:20:43 ultra.kervao.fr mosquitto[1436264]: 1765106443: New connection from 127.0.0.1:55164 on port 1883.
déc. 07 12:20:43 ultra.kervao.fr mosquitto[1436264]: 1765106443: New client connected from 127.0.0.1:55164 as auto-5B7917FC-A109-8461-E38C-B893A8928EA6 (p2, c1, k60, u'olivier').

and for detection

déc. 07 19:47:55 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Detected: camera=Camera-entree, label=person, id=1765133275.303314-xxsfrt
déc. 07 19:47:59 ultra.kervao.fr alerte_frigate[1596716]: [DEBUG] Trouvé après 3 seconde(s): /media/frigate/clips/Camera-entree-1765133275.303314-xxsfrt.jpg
déc. 07 19:47:59 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Clip annoté trouvé: /media/frigate/clips/Camera-entree-1765133275.303314-xxsfrt.jpg
déc. 07 19:47:59 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Clip candidate trouvé: /media/frigate/clips/Camera-entree-1765133275.303314-xxsfrt.jpg
déc. 07 19:48:00 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Fichier stable (taille=223910). Prêt à être envoyé.

The email content is as follows in case of a New Frigate detection alert.

Nouvelle détection Frigate.

Caméra : Camera-entree
Objet  : person
ID     : 1765133275.303314-xxsfrt

Payload brut :
{"before": {"id": "1765133275.303314-xxsfrt", "camera": "Camera-entree", "frame_time": 1765133275.303314, "snapshot": null, "label": "person", "sub_label": null, "top_score": 0.0, "false_positive": true, "start_time": 1765133275.303314, "end_time": null, "score": 0.89453125, "box": [832, 201, 907, 368], "area": 12525, "ratio": 0.4491017964071856, "region": [724, 143, 1044, 463], "active": true, "stationary": false, "motionless_count": 0, "position_changes": 0, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": false, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null}, "after": {"id": "1765133275.303314-xxsfrt", "camera": "Camera-entree", "frame_time": 1765133275.499867, "snapshot": {"frame_time": 1765133275.303314, "box": [832, 201, 907, 368], "area": 12525, "region": [724, 143, 1044, 463], "score": 0.89453125, "attributes": [], "current_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null, "recognized_license_plate_score": null}, "label": "person", "sub_label": null, "top_score": 0.8671875, "false_positive": false, "start_time": 1765133275.303314, "end_time": null, "score": 0.84765625, "box": [815, 201, 897, 364], "area": 13366, "ratio": 0.5030674846625767, "region": [716, 134, 1036, 454], "active": true, "stationary": false, "motionless_count": 0, "position_changes": 1, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": true, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [[[0.6687, 0.5056], 1765133275.499867]], "recognized_license_plate": null}, "type": "new"}


with the snapshot image attached. And here's another variation with an HTML email in the message body

### --- MAIL HTML ---
   TMPMAIL=$(mktemp)
   cat > "$TMPMAIL" <<EOF
<html>
<body style="font-family:Arial,sans-serif">
<h2 style="color:#c00">🚨 Alerte Frigate</h2>
<p>
<b>Caméra :</b> ${camera}<br>
<b>Objet détecté :</b> ${label}<br>
<b>ID événement :</b> ${EVENT_ID}<br>
<b>Date :</b> $(date +"%Y-%m-%d %H:%M:%S")
</p>
<p>Snapshot annoté :</p>
</body>
</html>
EOF

   s-nail \
     -S smtp="$SMTP_URL" \
     -S from="$MAIL_FROM" \
     -s "Alerte Frigate — ${label} (${camera})" \
     -a "$ATTACH" \
     -M "text/html" \
     "$MAIL_TO" < "$TMPMAIL"

   log "Mail envoyé avec snapshot : $CLIP_FILE"
   rm -f "$TMPMAIL"

Sending alerts with Signal

We now come to Signal, or rather signal-cli, the command-line version whose URL is https://github.comAsamK/signal-cli. I retrieved the binary, decompressed it, and placed it in the correct location:

tar xvfz signal-cli-0.14.1-Linux-native.tar.gz
mv signal-cli /usr/local/bin

We will also need the qrencode generator, which we will install via urpmi. Assuming you already have a Signal account, we will link the Frigate device to the main account on the mobile device by typing

signal-cli link -n "FrigateAlert"

This will give something like


█████████████████████████████████████████████████
█████████████████████████████████████████████████
████ ▄▄▄▄▄ █▀ ▄▀▄█▄▀ █ ▄█ ▄▄█▄█▀ ██▄ █ ▄▄▄▄▄ ████
████ █   █ █▀█ ▄▀█▄ ▄█▀█▄▄▀▀▄▀▀ ▀▀▄  █ █   █ ████
████ █▄▄▄█ █▀ ▀▀▄▄▄▄▄██▀▄▄█▄▀██ ▄█▄▄██ █▄▄▄█ ████
████▄▄▄▄▄▄▄█▄▀ █ █ █ █▄▀ ▀▄▀ █ █▄█▄▀▄█▄▄▄▄▄▄▄████
████▄    ▀▄▄ █▄█  ▀█▀ ▄▄▀ ▄█▄█ █▄ █▀▄ ▀ ▀ ▀ █████
████▄ ██▀█▄  ▄ ▀  ▄▄▄ ▀▀▄ ▄▀ ▄█▄▄███ █▀▀█▀▄██████
████  ▀█▀ ▄█▀█▄▄▀▀▄▀▄█▀█▄█▄▄▄▄█  ▄▄█▀▀▀▀ █   ████
█████▄▀▀ ▄▄  ▀▀█▀█  ▄▄▄█▄ █▀ ▄█▄███▀█▄ ▄ ███▀████
████▄█▄▄▄█▄█  ▄█▄█▄▄▀█▀▀▄▀ ███▄▄▄▄▀▀▀▀▀▀▀  ▀█████
████▄▀▀▀▀ ▄▄▀ ▀█▄█▀▄ █▄█  ▄█ █  ▀▄▄▀▄█▄▄▄████████
████▄▀██▄▀▄██ ▄█▄█▀▀▀█▄█▀▀▄ ▀▄▀ ▀ █▀▄▄▀  ▄▄▀▀████
████▀ ██▄▄▄ █▄  ▄▄██▀█▀ ▀ ███▄   █▄█ ▀█  ███▀████
████  ▄▄ ▀▄▄▄▀█ ▄▀ ▀    █  ▀▄▄▄ ▀▄▀▀▀▀▀▀▀▄ ▄▀████
████ █▀ █▄▄██ ▀█▄ ▄▄█ ▀█▄ ▄█▀▄█ ▀█▀█▄ ▄▀▄█▄▄▀████
████▀▀▀▀ ▀▄▄ ▀▀▄▀▀▄ ▄█▀█▄▀▀▀ ▀▄  ██ █▀█▀ █▄▄ ████
████ █ ▀▀▀▄██▄██▀▄█ ▄▄▀█▄  ▀▀▄▄▄██▀█▄███▄ ▄▀▀████
████▄████▄▄█ ▀█▀▄▄▀▄▀▄██  ▄▄▄█▄▀ ▄▄█ ▄▄▄ ▀ ▀ ████
████ ▄▄▄▄▄ █▄  █ █▀  █▄█▄ ▄▀█▄▄ ▄▄▄  █▄█ ▄▄▀▀████
████ █   █ █  ▀▄▄█▀ ▀█▄█▀ ██▄ ▀▀▀ ▀█ ▄▄▄▄█   ████
████ █▄▄▄█ █  █ ▄███▀█  ▀▀▄█ █▀▄███▄▀█▄ ▄ ▄▀█████
████▄▄▄▄▄▄▄█▄▄█▄█▄▄█▄▄█▄▄█▄▄▄█▄▄███▄██▄▄█████████
█████████████████████████████████████████████████
█████████████████████████████████████████████████

sgnl://linkdevice?uuid=ITD32aRGnIGBhZIrHpUFqQ%3D%3D&pub_key=BZ0yldJk5JH04kHEQGm8Ga%1B4b3tSaJUBZXb%2F8K%2Bpdee4


with the mobile phone we associate the Frigate unit by viewing the QR code thus obtained and in the first shell we have the following message

INFO  ProvisioningManagerImpl - Received link information from +336XXXXXXXX, linking in progress ...
Associated with: +336XXXXXXXX

Frigate is now correctly associated with the main Signal account. To check if it's working, you can type

signal-cli --username +336XXXXXXXX send -m "This is a message" 06YYYYYYYY

Now we can add it at the beginning of the script along with the other variables.

 SIGNAL_USER="+336XXXXXXXX"       # ton numéro Signal
 SIGNAL_DEST="+336YYYYYYYY"       # numéro qui reçoit les alertes qui peut être le même numéro, ça apparaitra dans les Notes perso

then further on, instead of sending by email

### --- ENVOI SIGNAL ---
   if [[ -n "$ATTACH" && -f "$ATTACH" ]]; then
       signal-cli \
               send -u "$SIGNAL_USER"\
               -g "Wti9r4I7n+76f(...)O3yLkUIgh3TAt9Y="\
               -a "$ATTACH"\
               -m "🚨 Alerte Frigate
               Caméra : ${camera}
               Objet  : ${label}
               ID     : ${EVENT_ID}
               Date   : $(date)"
       # si tmp file, on peut le supprimer
       if [[ "$ATTACH" == /tmp/frigate_snapshot_* ]]; then
          rm -f "$ATTACH"
       fi
       log "Alerte envoyée sur Signal au groupe Alarme avec pj : $ATTACH"
   else  
       signal-cli \
               send -u "$SIGNAL_USER"\
               -g "Wti9r4I7n+76f(...)O3yLkUIgh3TAt9Y="\
               -m "🚨 Alerte Frigate
               Caméra : ${camera}
               Objet  : ${label}
               ID     : ${EVENT_ID}
               Date   : $(date)"
       log "Alerte envoyée sur Signal au groupe Alarme sans pj"
   fi

And here's what it looks like when sent to my own number
  


In case of an alert that does not correspond to person or car, here is what journalctl will report

Dec 12 18:39:10 ultra.kervao.fr alerte_frigate[3356635]: [DEBUG] Event ignored for label=cat

Here's a variation for sending to a specific group where you can add other people. To list the groups, type

signal-cli listGroups

here is the result

INFO  AccountHelper - The Signal protocol expects that incoming messages are regularly received.
Id: 2BVGj6gu1es4K9(...)4BdAwavlpon8rE= Name: groupe1  Active: true Blocked: false
Id: vToJsVzeJeME(...)iyP+Xxf61plZA8= Name: groupe2  Active: true Blocked: false
Id: ZWLvp3vW(....)vYy5hse3hpTQ0= Name: groupe3  Active: true Blocked: false
Id: 9dhS9vM5(...)rVVUJSM/7jYIrIU84= Name: groupe4  Active: true Blocked: false
Id: puoaoo7(...)8hm62WWcMlFqo= Name: groupe5  Active: true Blocked: false

and we put the id of the group concerned, to send a message it will give

signal-cli --username +336XXXXXXXX send -g vToJsVzeJeME(...)iyP+Xxf61plZA8=  -m "This is a test message"

and we can adapt the script accordingly.

[ back to top of page ]

Frigate+ overview

Frigate+ is the paid option for Frigate. For a subscription of $50 (payable via PayPal) per year, the main advantage of this version is the ability to use a custom model that refines detection by classifying true and false positives, and also provides access to community models. Through continuous learning, the custom model becomes increasingly accurate and requires fewer and fewer resources.
The official page is https://plus.frigate.video. Once the subscription is paid, the first step is to create the API key and then set up the cameras.



In the Frigate configuration, we will now specify the key and enable snapshots. In the docker-compose.yml file, add the following to the environment section   :

environment:
    
  PLUS_API_KEY: your-API-key


In the
config.yaml file, add a snapshots section if it doesn't already exist:

snapshots:
 enabled: true
 timestamp: true
 bounding_box: true
 retain:
   objects:
     person: 7
 clean_copy: true

To apply the changes, stop
Frigate by typing docker stop frigate,  then

docker compose -f docker-compose.yml up -d

and restart
Frigate by typing docker start frigate. You should then see that Frigate+ is working correct



Now we click on Explore and in the filters, we select only the objects that have a snapshot and that have not yet been submitted to the Frigate+ centralized database.



Click on an image of an object and then on Snapshot ; confirm if the object detected in the bottom right of the image is correctly identified.

This screenshot shows an example of classification with the object's subcategory "Car" and the identified license plate. The image has been submitted to the Frigate+ database .


The image is submitted to the centralized server, and so on. We now go to
https://plus.frigate.video/dashboard/images/?tab=0 where we find all the submitted images. Clicking on an image displays the objects that have been detected.




If we agree, we click on Verify & Save



Now, if an object has not been detected, you can indicate it by clicking on Add (w) and place the detection box in the correct location, clearly identifying the object (in the image below there is no one, it is for illustration).



a fortiori if there is a false positive, we will click on the box to delete it and we will see that the object's label is crossed out.



We will now generate a custom model in addition to the basic Frigate+ model, which is itself enriched and trained by objects identified by the community. To create it, you need to upload at least ten images that have been properly annotated and verified, then enrich it by continuing to upload images. Ideally, you should have around a hundred images in all weather and lighting conditions for the outdoor cameras.
You can create up to 12 models per year based on different base models. Let's start to create the first model by clicking on Request Model.



I chose yolov9, which is considered better than mobiledet , and it's compatible with my TPU key.



And here's the result.





The Base Models tab lists the base models used by default.



Once the model generation is complete, which can be quite quick if you only have about ten images uploaded, you get an identifier. Below, it created two for me.



I limit myself to models for my TPU key and I specify its ID in the config.yaml file with a new section

model:
  path: plus://model-id

We restart Frigate so that the file is recognized, and here's what it looks like in the interface.

From then on, it picked up many more objects, like this cat, that I couldn't detect before.

When the model is well trained, we can review the object detection threshold settings to lower them in order to capture more objects.

Frigate+ allows you to generate up to 12 models per year. The advantage of generating multiple models is that, model by model, you can create refined models that precisely adapt to the cameras' environment. Successive models improve false positive detection, adapt to varying conditions (season, wind, lighting, etc.), and better detect certain objects (animals, stationary objects, etc.). Generating an additional model does not mean using multiple models until the system's resources are exhausted; the new model simply replaces the previous one.

Generating one model per month is a good pace; during the life of a model, it will be necessary to continue annotating the images; ideally, at least 200 to 300 annotated images are needed for it to be worthwhile to change models and for a real improvement to be observed.

A word of caution regarding image annotation: when submitting images, don't limit yourself to false positives, otherwise the model will become paranoid and stop detecting anything, or it will make mistakes in detection, for example, mistaking a person for a cat. Therefore, it's essential to also submit positive detections. The ideal ratio is 80% true positives and 20% false positives.

If it regularly detects a gatepost as a person, it will also be necessary to submit images where a real person walks past the gatepost so that it can see the difference; this is called contrastive learning .

To create a new model in the Frigate+ interface, click on Request Model




Then Continue, I chose the model adapted to the Coral key



It will take some time to generate it


We are notified by email when the model is ready.


Next, you need to modify the config.yaml file to indicate the identifier of the new model and restart frigate .

model:  path: plus://new-identifier

Note that if the base model evolves to a new version, it will be necessary to request the generation of a new customized model for it to be taken into account.

If the subscription is not renewed, access to the personalized template will still be available.

Here are some links to learn more