Merge pull request 'UCI to YML Syncing, Removed UCI Mask Config, General UX Improvements' (#3) from riley/luci-app-frigate:main into main

Reviewed-on: #3
main
jason 12 months ago
commit 8d5b4fb127

@ -34,6 +34,7 @@ o.default = "/dev/crypto"
o = s:option(Value, "storage", "Storage Path")
o.default = "./frigate/storage"
-- MQTT Configuration
s = m:section(NamedSection, "mqtt", "frigate_config", "MQTT Configuration")
s.addremove = false
@ -68,8 +69,18 @@ s.novaluetext = "There are no cameras configured yet."
o = s:option(Value, "name", "Camera Name")
o = s:option(Value, "path", "RTSP Path")
o = s:option(Value, "roles", "Roles")
o = s:option(Flag, "record", "Enable Recording")
o.default = "0"
o.rmempty = false
o = s:option(Flag, "snapshots", "Enable Snapshots")
o = s:option(Value, "mask", "Mask")
o.default = "0"
o.rmempty = false
-- Add the new flag for Overwrite Frigate Config
o = s:option(Flag, "overwrite_cfg", "Overwrite Frigate Config")
o.default = "1"
o.rmempty = false
return m

@ -14,27 +14,11 @@ config frigate_config 'tpu'
option type 'edgetpu'
option device 'usb'
config camera_config 'cam1'
config camera_config
option name 'living_room'
option path 'rtsp://admin:password@192.168.70.173:554/cam/realmonitor?channel=1&subtype=0&authbasic=64'
option roles 'detect'
option record '1'
option snapshots '1'
option mask '0,461,3,0,1919,0,1919,843,1699,492,1344,458,1346,336,973,317,869,375,866,432'
config camera_config 'cam2'
option name 'upstairs_bedroom'
option path 'rtsp://admin:password@192.168.70.173:554/cam/realmonitor?channel=1&subtype=0&authbasic=64'
option roles 'detect'
option record '1'
option snapshots '1'
option mask '0,461,3,0,1919,0,1919,843,1699,492,1344,458,1346,336,973,317,869,375,866,432'
config camera_config 'cam3'
option name 'kitchen'
option path 'rtsp://admin:password@192.168.70.173:554/cam/realmonitor?channel=1&subtype=0&authbasic=64'
option roles 'detect'
option record '1'
option snapshots '1'
option mask '0,461,3,0,1919,0,1919,843,1699,492,1344,458,1346,336,973,317,869,375,866,432'
option overwrite_cfg '1'

@ -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")

Loading…
Cancel
Save