@ -4,9 +4,20 @@ APP_NAME="frigate"
ACTION = " ${ 1 } "
shift 1
#set -x
get_image( ) {
do_install_detail
fetch_and_validate_uci ( ) {
local config_name = $1
local config_attr = $2
local result = $( uci get frigate.$config_name .$config_attr 2>/dev/null)
if [ -z " $result " ] ; then
echo " Error: $config_name not found in configuration. " >& 2
exit 1
fi
echo " Fetched $config_name from configuration: $result " >& 2
echo $result
}
do_install_detail( ) {
@ -14,44 +25,34 @@ do_install_detail() {
local usbcoral hwaccel storage mqtt host coral type device
echo "Fetching port from configuration..."
port = $( uci get frigate.docker.port 2>/dev/null)
[ -z " $port " ] && { echo "Error: Port not found in configuration." ; exit 1; }
port = $( fetch_and_validate_uci docker port)
echo "Fetching image name from configuration..."
IMAGE_NAME = $( uci get frigate.docker.image 2>/dev/null)
[ -z " $IMAGE_NAME " ] && { echo "Error: Image name not found in configuration." ; exit 1; }
IMAGE_NAME = $( fetch_and_validate_uci docker image)
echo "Fetching USB Coral path from configuration..."
usbcoral = $( uci get frigate.docker.usbcoral 2>/dev/null)
[ -z " $usbcoral " ] && { echo "Error: USB Coral path not found in configuration." ; exit 1; }
usbcoral = $( fetch_and_validate_uci docker usbcoral)
echo "Fetching hardware acceleration path from configuration..."
hwaccel = $( uci get frigate.docker.hwaccel 2>/dev/null)
[ -z " $hwaccel " ] && { echo "Error: Hardware acceleration path not found in configuration." ; exit 1; }
hwaccel = $( fetch_and_validate_uci docker hwaccel)
echo "Fetching storage path from configuration..."
storage = $( uci get frigate.docker.storage 2>/dev/null)
[ -z " $storage " ] && { echo "Error: Storage path not found in configuration." ; exit 1; }
storage = $( fetch_and_validate_uci docker storage)
echo "Fetching MQTT status from configuration..."
mqtt = $( uci get frigate.mqtt.enabled 2>/dev/null)
[ -z " $mqtt " ] && { echo "Error: MQTT status not found in configuration." ; exit 1; }
mqtt = $( fetch_and_validate_uci mqtt enabled)
echo "Fetching MQTT host from configuration..."
host = $( uci get frigate.mqtt.host 2>/dev/null)
[ -z " $host " ] && { echo "Error: MQTT host not found in configuration." ; exit 1; }
host = $( fetch_and_validate_uci mqtt host)
echo "Fetching Coral status from configuration..."
coral = $( uci get frigate.tpu.device 2>/dev/null)
[ -z " $coral " ] && { echo "Error: Coral status not found in configuration." ; exit 1; }
coral = $( fetch_and_validate_uci tpu device)
echo "Fetching Coral type from configuration..."
type = $( uci get frigate.tpu.type 2>/dev/null)
[ -z " $type " ] && { echo "Error: Coral type not found in configuration." ; exit 1; }
type = $( fetch_and_validate_uci tpu type )
echo "Fetching Coral device from configuration..."
device = $( uci get frigate.tpu.device 2>/dev/null)
[ -z " $device " ] && { echo "Error: Coral device not found in configuration." ; exit 1; }
device = $( fetch_and_validate_uci tpu device)
LAN_IP = $( uci get network.lan.ipaddr)
LAN_IP = " ${ LAN_IP %/* } "
@ -102,14 +103,13 @@ EOF
# yq eval ".usbcoral = \"$usbcoral\"" -i /opt/docker2/compose/frigate/config.yml
# yq eval ".hwaccel = \"$hwaccel\"" -i /opt/docker2/compose/frigate/config.yml
# yq eval ".media.storage = \"$storage\"" -i /opt/docker2/compose/frigate/config.yml
yq eval '. head_comment="WARNING: Values labeled OVERWRITTEN_BY_ROUTER are auto-managed by Private Router. Uncheck \"Overwrite Frigate Config\" in camera settings to edit manually."' -i /opt/docker2/compose/frigate/config.yml
yq eval " .mqtt.enabled = $mqtt " -i /opt/docker2/compose/frigate/config.yml
yq eval " .mqtt.host = \" $host \" " -i /opt/docker2/compose/frigate/config.yml
yq eval " .detectors.coral.type = \" $type \" " -i /opt/docker2/compose/frigate/config.yml
yq eval " .detectors.coral.device = \" $coral \" " -i /opt/docker2/compose/frigate/config.yml
# Initialize cameras structure
yq eval ".cameras = {}" -i /opt/docker2/compose/frigate/config.yml
# Write each camera's configuration to config.yml
local camera_index = 0
while : ; do
@ -117,45 +117,7 @@ EOF
local name = $( uci get frigate.@camera_config[ $camera_index ] .name 2>/dev/null | tr ' ' '_' )
# Exit loop if no more cameras are found
[ [ -z " $name " ] ] && break
# Retrieve the rest of the camera settings here...
local path = $( uci get frigate.@camera_config[ $camera_index ] .path 2>/dev/null)
local roles = $( uci get frigate.@camera_config[ $camera_index ] .roles 2>/dev/null)
local record = $( uci get frigate.@camera_config[ $camera_index ] .record 2>/dev/null)
local snapshots = $( uci get frigate.@camera_config[ $camera_index ] .snapshots 2>/dev/null)
local mask = $( uci get frigate.@camera_config[ $camera_index ] .mask 2>/dev/null)
# Initialize camera structure
yq eval " .cameras. $name = {} " -i /opt/docker2/compose/frigate/config.yml
# Initialize ffmpeg and inputs for each camera
yq eval " .cameras. $name .ffmpeg = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .ffmpeg.inputs = [] " -i /opt/docker2/compose/frigate/config.yml
# Add path and roles to the inputs
yq eval " .cameras. $name .ffmpeg.inputs[0].path = \" $path \" " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .ffmpeg.inputs[0].roles = [\"detect\"] " -i /opt/docker2/compose/frigate/config.yml
# Add other camera settings
yq eval " .cameras. $name .detect = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .detect.width = 1280 " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .detect.height = 720 " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .record = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .record.enabled = $record " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .snapshots = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .snapshots.enabled = $snapshots " -i /opt/docker2/compose/frigate/config.yml
# Check if the mask is not empty
if [ -n " $mask " ] ; then
# Run the yq commands to update the YAML file
yq eval " .cameras. $name .motion = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .motion.mask = [\" $mask \"] " -i /opt/docker2/compose/frigate/config.yml
else
echo "Mask is empty, ignoring."
fi
write_camera_to_yml $name
camera_index = $(( camera_index+1))
done
@ -178,6 +140,197 @@ usage() {
echo " port frigate port"
}
# Gets a specific setting for a camera. If no setting is provided, it retrieves the camera name.
get_uci_camera_value( ) {
local index = $1
local setting = ${ 2 :- name }
uci get frigate.@camera_config[ $index ] .$setting 2>/dev/null | tr ' ' '_'
}
# Sets a specific setting for a camera
set_uci_camera_value( ) {
local name = $1
local setting = $2
local value = $3
uci set frigate.@camera_config[ @$name ] .$setting = $value
uci commit frigate
}
write_camera_to_yml( ) {
local name = $1
local camera_index = ""
local lines = $( uci show frigate | grep '@camera_config' )
IFS = $'\n'
for line in $lines
do
if [ [ $line = = *" .name=' $name ' " * ] ]
then
camera_index = $( echo $line | grep -E -o "@camera_config\[[0-9]+\]" | grep -E -o "[0-9]+" )
break
fi
done
if [ [ -z " $camera_index " ] ]
then
echo " Camera $name not found in UCI "
return
fi
# Retrieves the rest of the camera settings from UCI...
local path = $( uci get frigate.@camera_config[ $camera_index ] .path 2>/dev/null)
local roles = $( uci get frigate.@camera_config[ $camera_index ] .roles 2>/dev/null)
local record = $( uci get frigate.@camera_config[ $camera_index ] .record 2>/dev/null)
local snapshots = $( uci get frigate.@camera_config[ $camera_index ] .snapshots 2>/dev/null)
# Initialize camera structure in YML
yq eval " .cameras. $name = {} " -i /opt/docker2/compose/frigate/config.yml
# Initialize ffmpeg and inputs for each camera in YML
yq eval " .cameras. $name .ffmpeg = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .ffmpeg.inputs = [] " -i /opt/docker2/compose/frigate/config.yml
# Add path and roles to the inputs in YML
yq eval " .cameras. $name .ffmpeg.inputs[0].path = \" $path \" " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .ffmpeg.inputs[0].roles = [\"detect\"] " -i /opt/docker2/compose/frigate/config.yml
# Add other camera settings to YML
yq eval " .cameras. $name .detect = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .detect.width = 1280 " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .detect.height = 720 " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .record = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .record.enabled = $record " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .snapshots = {} " -i /opt/docker2/compose/frigate/config.yml
yq eval " .cameras. $name .snapshots.enabled = $snapshots " -i /opt/docker2/compose/frigate/config.yml
add_warning_comments $name
}
get_camera_index_by_name( ) {
local name = $1
local camera_index = 0
while : ; do
local current_name = $( get_uci_camera_value $camera_index )
[ [ " $current_name " = = " $name " ] ] && echo $camera_index && break
[ [ -z " $current_name " ] ] && break
camera_index = $(( camera_index+1))
done
}
# Toggles the line comment for a specific camera attribute in the YML
add_warning_comments( ) {
local name = $1
yq eval '(.cameras.' $name ' | key) line_comment="DO NOT REMOVE - Managed by PrivateRouter Script"' -i /opt/docker2/compose/frigate/config.yml
yq eval '(.cameras.' $name '.ffmpeg.inputs[0].path | key) line_comment="OVERWRITTEN_BY_ROUTER"' -i /opt/docker2/compose/frigate/config.yml
yq eval '(.cameras.' $name '.ffmpeg.inputs[0].roles | key) line_comment="OVERWRITTEN_BY_ROUTER"' -i /opt/docker2/compose/frigate/config.yml
yq eval '.cameras.' $name '.record.enabled line_comment="OVERWRITTEN_BY_ROUTER"' -i /opt/docker2/compose/frigate/config.yml
yq eval '.cameras.' $name '.snapshots.enabled line_comment="OVERWRITTEN_BY_ROUTER"' -i /opt/docker2/compose/frigate/config.yml
}
remove_warning_comments( ) {
local name = $1
yq eval '.cameras.' $name '.ffmpeg.inputs[0].path line_comment=""' -i /opt/docker2/compose/frigate/config.yml
yq eval '(.cameras.' $name '.ffmpeg.inputs[0].roles | key) line_comment=""' -i /opt/docker2/compose/frigate/config.yml
yq eval '.cameras.' $name '.record.enabled line_comment=""' -i /opt/docker2/compose/frigate/config.yml
yq eval '.cameras.' $name '.snapshots.enabled line_comment=""' -i /opt/docker2/compose/frigate/config.yml
}
update_camera_in_yml( ) {
local name = $1
# Get the index of the camera with name "$name"
local camera_index = $( get_camera_index_by_name $name )
# Fetch and Update the specific settings for this camera from UCI -> YAML
# For each attribute you update, apply warning comment
local path = $( get_uci_camera_value $camera_index "path" )
yq eval '.cameras.' $name '.ffmpeg.inputs[0].path = "' $path '"' -i /opt/docker2/compose/frigate/config.yml
local roles = "[\"detect\"]" # adjust as necessary
yq eval '.cameras.' $name '.ffmpeg.inputs[0].roles = ' $roles '' -i /opt/docker2/compose/frigate/config.yml
local record = $( get_uci_camera_value $camera_index "record" )
yq eval '.cameras.' $name '.record.enabled = ' $record '' -i /opt/docker2/compose/frigate/config.yml
local snapshots = $( get_uci_camera_value $camera_index "snapshots" )
yq eval '.cameras.' $name '.snapshots.enabled = ' $snapshots '' -i /opt/docker2/compose/frigate/config.yml
add_warning_comments $name
}
sync_camera_config( ) {
echo "Syncing camera config..."
# Initializing the camera index
camera_index = 0
# Fetching all cameras from frigate YML
yml_cameras = $( yq eval '... comments="" | .cameras | keys' /opt/docker2/compose/frigate/config.yml | sed 's/- //g' | xargs)
echo " Cameras fetched from YML: $yml_cameras "
# Loop through all camera configs in UCI using index
while true; do
# Try to fetch camera configuration
local camera_name = $( get_uci_camera_value $camera_index )
local overwrite_cfg = $( get_uci_camera_value $camera_index "overwrite_cfg" )
# Exit loop if no more cameras are found
[ -z " $camera_name " ] && break
# Only proceed if overwrite_cfg is enabled (set to 1)
if [ " $overwrite_cfg " -eq 1 ]
then
if ! echo " $yml_cameras " | grep -q -w " $camera_name " ; then
echo " UCI Camera $camera_name not found in YML, adding... "
write_camera_to_yml $camera_name
else
echo " UCI Camera $camera_name found in YML, updating... "
update_camera_in_yml $camera_name
fi
else
echo " Skipping camera $camera_name , overwrite is set to false. "
remove_warning_comments $camera_name
fi
# Add to uci_cameras
uci_cameras = " ${ uci_cameras } ${ camera_name } "
# Increment camera index
camera_index = $(( camera_index+1))
done
# Crosschecking and removing orphaned entry in YML not present in UCI config
for yml_camera in $yml_cameras ; do
# Get the comment of this camera
local comment = $( yq " .cameras. $yml_camera | key | line_comment " /opt/docker2/compose/frigate/config.yml)
comment = $( echo $comment | awk '{print tolower($0)}' ) # convert to lowercase using awk
comment = $( echo $comment | tr -d ' ' ) # remove whitespaces
if ! echo " $uci_cameras " | grep -q -w " $yml_camera " ; then
# If the comment is either non-existent or doesn't contain "donotremove-managedbyprivaterouterscript", keep it
if [ -z " $comment " ] || [ [ " $comment " != *"donotremove-managedbyprivaterouterscript" * ] ] ; then
echo " Manual camera $yml_camera found in YML. Keeping it since it's manually added. "
else
echo " Auto generated camera $yml_camera found in YML, but not in UCI , removing... "
yq eval " del(.cameras. $yml_camera ) " -i /opt/docker2/compose/frigate/config.yml
fi
fi
done
echo "Camera config syncing completed!"
}
case " ${ ACTION } " in
"install" | "upgrade" )
do_install_detail
@ -198,6 +351,7 @@ case "${ACTION}" in
CONTAINER_IDS = $( docker ps -a --filter " name= ${ APP_NAME } " --format '{{.ID}}' )
for ID in $CONTAINER_IDS ; do
docker " ${ ACTION } " " ${ ID } "
sync_camera_config
done
; ;
"status" )