From 1bf23bf3560a9272863a141afc73dc7a64073285 Mon Sep 17 00:00:00 2001 From: riley Date: Wed, 27 Sep 2023 00:40:56 -0400 Subject: [PATCH] Major refactor of frigate.sh Adds the ability to sync UCI camera values to the frigate config.yml --- root/usr/libexec/apps/frigate/frigate.sh | 246 +++++++++++++++++------ 1 file changed, 181 insertions(+), 65 deletions(-) diff --git a/root/usr/libexec/apps/frigate/frigate.sh b/root/usr/libexec/apps/frigate/frigate.sh index 56314d3..99ca06f 100755 --- a/root/usr/libexec/apps/frigate/frigate.sh +++ b/root/usr/libexec/apps/frigate/frigate.sh @@ -5,51 +5,55 @@ APP_NAME="frigate" ACTION="${1}" shift 1 +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." + exit 1 + fi + + echo "Fetched $config_name from configuration: $result" + echo $result +} + do_install_detail() { local config port IMAGE_NAME LAN_IP 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=$(fetch_and_validate_uci network.lan) LAN_IP="${LAN_IP%/*}" [ -z "$port" ] && port=1880 @@ -103,6 +107,7 @@ EOF 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 @@ -174,61 +179,169 @@ usage() { echo " port frigate port" } -sync_camera_config() { - echo "Starting sync_camera_config..." - - # First, handle additions: - local camera_index=0 - while : ; do - local name=$(uci get frigate.@camera_config[$camera_index].name 2>/dev/null | tr ' ' '_') - if [[ -z "$name" ]]; then - echo "No more cameras found in UCI at index $camera_index. Moving to deletions..." +# 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 +} + +# YML Helper Functions + +# Retrieves a specific setting from the YML. If no setting_path is provided, it retrieves the camera name. +get_yml_value() { + local name=$1 + local setting_path=${2:-} + yq eval ".cameras.$name$setting_path" /opt/docker2/compose/frigate/config.yml +} + +# Sets a specific setting in the YML +set_yml_value() { + local name=$1 + local setting_path=$2 + local value=$3 + # If the value starts with "{", "[", or is a number, we don't quote it; otherwise, we do. + case "$value" in + "{"*|"["*|*[0-9]*) + # do nothing + ;; + *) + value="\"$value\"" + ;; + esac + yq eval ".cameras.$name$setting_path = $value" -i /opt/docker2/compose/frigate/config.yml +} + +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 - echo "Processing UCI camera named: $name" - - # Check if the camera is in the YML - if ! yq eval "has(.cameras.$name)" /opt/docker2/compose/frigate/config.yml; then - echo "Camera $name not found in YML, adding..." - - # Initialize camera structure in YML - yq eval ".cameras.$name = {}" -i /opt/docker2/compose/frigate/config.yml - echo "Initialized $name in YML" - # ... Add other camera configurations here as needed ... - - else - echo "Camera $name already exists in YML. Skipping addition." - fi - camera_index=$((camera_index+1)) done - # Then, handle deletions: - local num_cameras=$(yq eval '.cameras | length' /opt/docker2/compose/frigate/config.yml 2>/dev/null) - local i=0 - local cameras_in_yml="" + 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) + local mask=$(uci get frigate.@camera_config[$camera_index].mask 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 + + # 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 +} + +update_camera_in_yml() { + local name=$1 + # Deletes the camera from the YML file and writes it again + yq eval "del(.cameras.$name)" -i /opt/docker2/compose/frigate/config.yml + write_camera_to_yml $name +} + + - while [ "$i" -lt "$num_cameras" ]; do - local camera_name=$(yq eval ".cameras | keys[$i]" /opt/docker2/compose/frigate/config.yml 2>/dev/null) - cameras_in_yml="$cameras_in_yml $camera_name" - i=$((i+1)) + +sync_camera_config() { + echo "Starting sync_camera_config..." + + # Fetching all cameras from UCI + local uci_cameras="" + local camera_index=0 + while : ; do + local name=$(get_uci_camera_value $camera_index) + # Exit loop if no more cameras are found + [[ -z "$name" ]] && break + + uci_cameras="$uci_cameras $name" + camera_index=$((camera_index+1)) done - echo "Checking for cameras in YML that are not in UCI..." - for camera_name in $cameras_in_yml; do - echo "Validating camera: $camera_name in UCI" - if ! uci get frigate.@camera_config[*].name | grep -q "^$camera_name$"; then - echo "Camera $camera_name not found in UCI, removing from YML..." - yq eval "del(.cameras.$camera_name)" -i /opt/docker2/compose/frigate/config.yml - echo "Removed $camera_name from YML" - else - echo "Camera $camera_name found in UCI. No deletion needed." - fi + echo "Cameras fetched from UCI: $uci_cameras" + + # Fetching all cameras from Frigate YML + local yml_cameras=$(yq eval '.cameras | keys' /opt/docker2/compose/frigate/config.yml | sed 's/- //g' | xargs) + + echo "Cameras fetched from YML: $yml_cameras" + + # Checking for cameras in UCI that are not in YML and adding them + for camera_name in $uci_cameras; do + if ! echo "$yml_cameras" | grep -q "$camera_name"; then + echo "Camera $camera_name not found in YML, adding..." + # Retrieves camera settings from UCI configuration and adds them to the YML file. + write_camera_to_yml $camera_name + else + echo "Camera $camera_name found in YML, updating..." + # Retrieves camera settings from UCI configuration and updates them in the YML file. + update_camera_in_yml $camera_name + fi done - echo "sync_camera_config completed!" + # Checking for cameras in YML that are not in UCI and deleting them + if [[ $yml_cameras != '[]' ]]; then # Add this condition + for camera_name in $yml_cameras; do + if ! echo "$uci_cameras" | grep -q "$camera_name"; then + echo "Camera $camera_name not found in UCI, removing from YML..." + yq eval "del(.cameras.$camera_name)" -i /opt/docker2/compose/frigate/config.yml + fi + done + fi # Close the condition + + echo "sync_camera_config completed!" } + + + case "${ACTION}" in "install" | "upgrade") do_install_detail @@ -268,4 +381,7 @@ case "${ACTION}" in # Use the UCI port to filter the docker ps output and return only the external port docker ps --all -f "name=${CONTAINER_NAME}" --format '{{.Ports}}' | grep -o "0.0.0.0:$uci_port->[0-9]*/tcp" | sed "s/0.0.0.0://;s/->[0-9]*\/tcp//" ;; + "sync") + sync_camera_config + ;; esac \ No newline at end of file