app/frigate: Add schema, templates for Frigate

[Frigate] is an open source network video recording software with
advanced motion detection using machine learning object detection.  It
uses `ffmpeg` to stream video from one or more RTSP-capable IP video
cameras and passes the images through an object detection process.  To
improve the performance of the machine learning model, it supports using
a Coral EdgeTPU device, which requires special drivers: `gasket` and
`apex`.

Frigate is configured via a (rather compex) YAML document, some of the
schema of which is modeled in `schema.cue` (the parts I need, anyway).

[Frigate]: https://frigate.video/
master
Dustin 2024-04-05 15:45:13 -05:00
parent c4dcb5a8de
commit cd64b3bccb
9 changed files with 286 additions and 0 deletions

View File

@ -0,0 +1,132 @@
package schema
#Frigate: {
env?: [string]: string
config: #FrigateConfig
}
#FrigateConfig: {
ffmpeg?: #FfmpegConfig
cameras: [string]: #CameraConfig
go2rtc?: #Go2RtcConfig
detectors?: [string]: #DetectorConfig
birdseye?: #BirdseyeConfig
mqtt: #MqttConfig
}
#FfmpegConfig: {
hwaccel_args?: string
}
#CameraConfig: {
detect?: {
height: int
width: int
}
ffmpeg: {
inputs: [...#FfmpegInput]
}
objects?: {
track: [...string]
filters?: [string]: #ObjectFilter
}
record?: #RecordConfig
rtmp?: {
enabled?: bool
}
snapshots?: #SnapshotsConfig
zones?: [string]: #ZoneConfig
motion?: {
mask: [...string]
}
}
#FfmpegInput: {
path: string
input_args?: string
roles: [...string]
}
#ObjectFilter: {
threshold?: float
}
#RecordConfig: {
enabled?: bool
retain?: {
days: int
mode?: #RetainMode
}
events: {
pre_capture?: int
post_capture?: int
objects?: [...string]
required_zones?: [...string]
retain?: {
default?: int
mode?: #RetainMode
objects?: [string]: int
}
}
}
#SnapshotsConfig: {
enabled?: bool
required_zones?: [...string]
retain?: {
default?: int
objects?: [string]: int
}
}
#RetainMode: "all" | "motion" | "active_objects"
#ZoneConfig: {
coordinates: string
inertia?: int
objects?: [...string]
filters?: [string]: {
min_area?: int
maax_area?: int
threshold?: float
}
}
#Go2RtcConfig: {
streams: [string]: [...string]
}
#DetectorConfig: #CpuDetector | #EdgeTpuDetector
#CpuDetector: {
type: "cpu"
num_threads?: int
}
#EdgeTpuDetector: {
type: "edgetpu"
device: string
}
#BirdseyeConfig: {
enabled?: bool
restream?: bool
width?: int
height?: int
quality?: int
mode?: "objects" | "motion" | "continuous"
inactivity_threshold?: int
layout?: {
scaling_factor?: float
max_cameras?: int
}
}
#MqttConfig: {
host: string
password?: string
port?: int
tls_ca_certs?: string
user?: string
}

68
app/frigate/templates.cue Normal file
View File

@ -0,0 +1,68 @@
package frigate
import "du5t1n.me/cfg/base/schema/instructions"
templates: [...instructions.#RenderInstruction] & [
{
template: "frigate/frigate.sysusers"
dest: "/etc/sysusers.d/frigate.conf"
hooks: changed: [
{
run: "systemd-sysusers /etc/sysusers.d/frigate.conf"
immediate: true
},
]
},
{
template: "frigate/frigate.env"
dest: "/etc/frigate/environ"
mode: "u=rw,go="
hooks: {
changed: [{run: "systemctl restart frigate"}]
}
},
{
template: "frigate/config.yml"
dest: "/etc/frigate/config.yml"
hooks: {
changed: [{run: "systemctl restart frigate"}]
}
},
{
template: "frigate/65-apex.rules"
dest: "/etc/udev/rules.d/65-apex.rules"
hooks: {
changed: [{run: "udevadm control --reload", immediate: true}]
}
},
{
template: "frigate/gasket-driver.container"
dest: "/etc/containers/systemd/gasket-driver.container"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
{run: "systemctl restart gasket-driver"},
]
}
},
{
template: "frigate/frigate.container"
dest: "/etc/containers/systemd/frigate.container"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
{run: "systemctl restart frigate"},
]
}
},
{
template: "frigate/frigate-watchdog.container"
dest: "/etc/containers/systemd/frigate-watchdog.container"
hooks: {
changed: [
{run: "systemctl daemon-reload", immediate: true},
{run: "systemctl restart frigate-watchdog"},
]
}
},
]

View File

@ -0,0 +1,5 @@
# Add systemd tag to apex devices so systemd will create .device
# units for them. This allows Frigate to wait for the device to be
# available before starting, by adding an After= dependency on the
# .device unit.
SUBSYSTEM=="apex", MODE="0666", TAG+="systemd"

View File

@ -0,0 +1 @@
{{ frigate.yaml -}}

View File

@ -0,0 +1,18 @@
[Unit]
Description=Frigate watchdog
After=frigate.service
[Container]
Image=git.pyrocufflink.blue/containerimages/frigate-watchdog
Pull=newer
Exec=-u frigate
Volume=/run/log:/run/log:ro
Volume=/var/log:/var/log:ro
Volume=/etc/machine-id:/etc/machine-id:ro
ReadOnly=true
VolatileTmp=true
PodmanArgs=--privileged --uts=host --cgroupns=host --ipc=host --pid=host
Network=host
[Install]
WantedBy=frigate.service

View File

@ -0,0 +1,34 @@
[Unit]
Description=Frigate NVR
Wants=network-online.target
After=network-online.target
Requires=dev-apex_0.device
After=dev-apex_0.device
RequiresMountsFor=/var/lib/frigate
[Container]
Image=ghcr.io/blakeblackshear/frigate:0.12.1
PodmanArgs=--uidmap 0:209:1
PodmanArgs=--gidmap 0:209:1
PodmanArgs=--uidmap 1:6000001:65536
PodmanArgs=--gidmap 1:6000001:65536
PodmanArgs=--shm-size 256m
EnvironmentFile=/etc/frigate/environ
Volume=/var/lib/frigate/media:/media/frigate:rw,z,U
Volume=/var/lib/frigate/tmp:/tmp:rw,z,U
Volume=/etc/frigate/config.yml:/config/config.yml:ro
AddDevice=/dev/apex_0
AddDevice=/dev/dri/renderD128
AddCapability=CAP_PERFMON
Network=host
Annotation=org.systemd.property.KillMode='none'
[Service]
UMask=0077
Restart=always
TimeoutStopSec=infinity
StateDirectory=%N/tmp
StateDirectory=%N/media
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,9 @@
{% if frigate.env is defined -%}
{% for k, v in frigate.env -%}
{% if v is starting_with("-----BEGIN AGE") -%}
{{ k }}={{ v | decrypt }}
{% else %}
{{ k }}={{ v }}
{% endif -%}
{% endfor -%}
{% endif -%}

View File

@ -0,0 +1,2 @@
g frigate 209
u frigate 209:209 "Frigate" /var/lib/frigate /sbin/nologin

View File

@ -0,0 +1,17 @@
[Unit]
Description=Install the gasket/apex kernel modules
Wants=network-online.target
After=network-online.target
[Container]
Image=git.pyrocufflink.net/containerimages/gasket-driver:%v
PodmanArgs=--privileged
[Service]
Type=oneshot
RemainAfterExit=true
ExecStop=/usr/sbin/rmmod apex
ExecStop=/usr/sbin/rmmod gasket
[Install]
WantedBy=multi-user.target