From 4bfc9a951470938a67140d4f7ddc8baebd4e6fa6 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Mon, 22 Sep 2025 19:01:52 +0200 Subject: [PATCH 1/3] Add backing up of ir.json to wled-tools --- tools/wled-tools | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/wled-tools b/tools/wled-tools index 9d196526..573aa57f 100755 --- a/tools/wled-tools +++ b/tools/wled-tools @@ -115,12 +115,15 @@ backup_one() { local cfg_url="http://$address:$port/cfg.json" local presets_url="http://$address:$port/presets.json" + local ir_url="http://$address:$port/ir.json" local cfg_dest="${backup_dir}/${hostname}.cfg.json" local presets_dest="${backup_dir}/${hostname}.presets.json" + local ir_dest="${backup_dir}/${hostname}.ir.json" # Write to ".tmp" files first, then move when success, to ensure we don't write partial files local curl_command_cfg="curl -s "$cfg_url" -o "$cfg_dest.tmp"" local curl_command_presets="curl -s "$presets_url" -o "$presets_dest.tmp"" + local curl_command_ir="curl -s "$ir_url" -o "$ir_dest.tmp"" if ! curl_handler "$curl_command_cfg" "$hostname"; then log "ERROR" "$RED" "Failed to backup configuration for $hostname" @@ -132,10 +135,18 @@ backup_one() { log "ERROR" "$RED" "Failed to backup presets for $hostname" rm -f "$presets_dest.tmp" return 1 - fi + fi mv "$cfg_dest.tmp" "$cfg_dest" mv "$presets_dest.tmp" "$presets_dest" + + if curl_handler "$curl_command_ir" "$hostname"; then + mv "$ir_dest.tmp" "$ir_dest" + else + rm -f "$ir_dest.tmp" + log "WARN" "$YELLOW" "No ir.json found for $hostname (skipping)" + fi + log "INFO" "$GREEN" "Successfully backed up config and presets for $hostname" return 0 } From 640d0ee1334936e4dc94d509cf53b2576d794634 Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Mon, 22 Sep 2025 20:52:08 +0200 Subject: [PATCH 2/3] Updates --- tools/wled-tools | 116 +++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 44 deletions(-) diff --git a/tools/wled-tools b/tools/wled-tools index 573aa57f..804f9548 100755 --- a/tools/wled-tools +++ b/tools/wled-tools @@ -28,28 +28,74 @@ log() { fi } -# Generic curl handler function -curl_handler() { - local command="$1" - local hostname="$2" +# Fetch a URL to a destination file, validating status codes. +# Usage: fetch "" "" "200 404" +fetch() { + local url="$1" + local dest="$2" + local accepted="${3:-200}" - response=$($command -w "%{http_code}" -o /dev/null) - curl_exit_code=$? - - if [ "$response" -ge 200 ] && [ "$response" -lt 300 ]; then - return 0 - elif [ $curl_exit_code -ne 0 ]; then - log "ERROR" "$RED" "Connection error during request to $hostname (curl exit code: $curl_exit_code)." - return 1 - elif [ "$response" -ge 400 ]; then - log "ERROR" "$RED" "Server error during request to $hostname (HTTP status code: $response)." - return 2 + # If no dest given, just discard body + local out + if [ -n "$dest" ]; then + # Write to ".tmp" files first, then move when success, to ensure we don't write partial files + out="${dest}.tmp" else - log "ERROR" "$RED" "Unexpected response from $hostname (HTTP status code: $response)." - return 3 + out="/dev/null" fi + + response=$(curl --connect-timeout 5 --max-time 30 -s -w "%{http_code}" -o "$out" "$url") + local curl_exit_code=$? + + if [ $curl_exit_code -ne 0 ]; then + [ -n "$dest" ] && rm -f "$out" + log "ERROR" "$RED" "Connection error during request to $url (curl exit code: $curl_exit_code)." + return 1 + fi + + for code in $accepted; do + if [ "$response" -eq "$code" ]; then + # success → move tmp into place + if [ -n "$dest" ]; then + mv "$out" "$dest" + fi + return 0 + fi + done + + # not accepted + [ -n "$dest" ] && rm -f "$out" + log "ERROR" "$RED" "Unexpected response from $url (HTTP $response)." + return 2 } + +# POST a file to a URL, validating status codes. +# Usage: post_file "" "" "200" +post_file() { + local url="$1" + local file="$2" + local accepted="${3:-200}" + + response=$(curl --connect-timeout 5 --max-time 300 -s -w "%{http_code}" -o /dev/null -X POST -F "file=@$file" "$url") + local curl_exit_code=$? + + if [ $curl_exit_code -ne 0 ]; then + log "ERROR" "$RED" "Connection error during POST to $url (curl exit code: $curl_exit_code)." + return 1 + fi + + for code in $accepted; do + if [ "$response" -eq "$code" ]; then + return 0 + fi + done + + log "ERROR" "$RED" "Unexpected response from $url (HTTP $response)." + return 2 +} + + # Print help message show_help() { cat << EOF @@ -109,44 +155,27 @@ backup_one() { local address="$2" local port="$3" - log "INFO" "$YELLOW" "Backing up device config/presets: $hostname ($address:$port)" + log "INFO" "$YELLOW" "Backing up device config/presets/ir: $hostname ($address:$port)" mkdir -p "$backup_dir" - local cfg_url="http://$address:$port/cfg.json" - local presets_url="http://$address:$port/presets.json" - local ir_url="http://$address:$port/ir.json" - local cfg_dest="${backup_dir}/${hostname}.cfg.json" - local presets_dest="${backup_dir}/${hostname}.presets.json" - local ir_dest="${backup_dir}/${hostname}.ir.json" + local file_prefix="${backup_dir}/${hostname}" - # Write to ".tmp" files first, then move when success, to ensure we don't write partial files - local curl_command_cfg="curl -s "$cfg_url" -o "$cfg_dest.tmp"" - local curl_command_presets="curl -s "$presets_url" -o "$presets_dest.tmp"" - local curl_command_ir="curl -s "$ir_url" -o "$ir_dest.tmp"" - - if ! curl_handler "$curl_command_cfg" "$hostname"; then + if ! fetch "http://$address:$port/cfg.json" "${file_prefix}.cfg.json"; then log "ERROR" "$RED" "Failed to backup configuration for $hostname" - rm -f "$cfg_dest.tmp" return 1 fi - if ! curl_handler "$curl_command_presets" "$hostname"; then + if ! fetch "http://$address:$port/presets.json" "${file_prefix}.presets.json"; then log "ERROR" "$RED" "Failed to backup presets for $hostname" - rm -f "$presets_dest.tmp" return 1 fi - mv "$cfg_dest.tmp" "$cfg_dest" - mv "$presets_dest.tmp" "$presets_dest" - - if curl_handler "$curl_command_ir" "$hostname"; then - mv "$ir_dest.tmp" "$ir_dest" - else - rm -f "$ir_dest.tmp" - log "WARN" "$YELLOW" "No ir.json found for $hostname (skipping)" + # ir.json is optional + if ! fetch "http://$address:$port/ir.json" "${file_prefix}.ir.json" "200 404"; then + log "ERROR" "$RED" "Failed to backup ir configs for $hostname" fi - + log "INFO" "$GREEN" "Successfully backed up config and presets for $hostname" return 0 } @@ -161,9 +190,8 @@ update_one() { log "INFO" "$YELLOW" "Starting firmware update for device: $hostname ($address:$port)" local url="http://$address:$port/update" - local curl_command="curl -s -X POST -F "file=@$firmware" "$url"" - if ! curl_handler "$curl_command" "$hostname"; then + if ! post_file "$url" "$firmware" "200"; then log "ERROR" "$RED" "Failed to update firmware for $hostname" return 1 fi From 9152d9d2ed268b0c6cc613eb561bbf387cd8609c Mon Sep 17 00:00:00 2001 From: Michael Bisbjerg Date: Mon, 22 Sep 2025 21:16:02 +0200 Subject: [PATCH 3/3] Accept change by coderabbit, move only 2xx files --- tools/wled-tools | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/wled-tools b/tools/wled-tools index 804f9548..34fb6370 100755 --- a/tools/wled-tools +++ b/tools/wled-tools @@ -54,10 +54,14 @@ fetch() { fi for code in $accepted; do - if [ "$response" -eq "$code" ]; then - # success → move tmp into place + if [ "$response" = "$code" ]; then + # Accepted; only persist body for 2xx responses if [ -n "$dest" ]; then - mv "$out" "$dest" + if [[ "$response" =~ ^2 ]]; then + mv "$out" "$dest" + else + rm -f "$out" + fi fi return 0 fi