-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Create wled-tools #4625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
LordMike
wants to merge
11
commits into
wled:main
Choose a base branch
from
LordMike:patch-1
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Create wled-tools #4625
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
ff36808
Create wled-tools.sh
LordMike 0139c34
Rename script in help text
LordMike 8a6e3a9
Update wled-tools.sh
LordMike 6f54827
Update wled-tools.sh
LordMike dc5eaf3
Rename file
LordMike 646ec30
Implement error checking for curl; add checks for required param args
LordMike bc099ba
Guard against hostnames/avahi responses with spaces
LordMike 5203c39
Update tools/wled-tools
LordMike 9e96bd6
Add help examples, header
LordMike 81b7422
Set executable
LordMike 88aa8e8
Add note on avahi-utils
LordMike File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
#!/bin/bash | ||
|
||
# WLED Tools | ||
# A utility for managing WLED devices in a local network | ||
# https://github.com/wled/WLED | ||
|
||
# Color Definitions | ||
GREEN="\e[32m" | ||
RED="\e[31m" | ||
BLUE="\e[34m" | ||
YELLOW="\e[33m" | ||
RESET="\e[0m" | ||
|
||
# Logging function | ||
log() { | ||
local category="$1" | ||
local color="$2" | ||
local text="$3" | ||
|
||
if [ "$quiet" = true ]; then | ||
return | ||
fi | ||
|
||
if [ -t 1 ]; then # Check if output is a terminal | ||
echo -e "${color}[${category}]${RESET} ${text}" | ||
else | ||
echo "[${category}] ${text}" | ||
fi | ||
} | ||
|
||
# Generic curl handler function | ||
curl_handler() { | ||
local command="$1" | ||
local hostname="$2" | ||
|
||
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 | ||
else | ||
log "ERROR" "$RED" "Unexpected response from $hostname (HTTP status code: $response)." | ||
return 3 | ||
fi | ||
} | ||
|
||
# Print help message | ||
show_help() { | ||
cat << EOF | ||
Usage: wled-tools.sh [OPTIONS] COMMAND [ARGS...] | ||
|
||
Options: | ||
-h, --help Show this help message and exit. | ||
-t, --target <IP/Host> Specify a single WLED device by IP address or hostname. | ||
-D, --discover Discover multiple WLED devices using mDNS. | ||
-d, --directory <Path> Specify a directory for saving backups (default: working directory). | ||
-f, --firmware <File> Specify the firmware file for updating devices. | ||
-q, --quiet Suppress logging output (also makes discover output hostnames only). | ||
|
||
Commands: | ||
backup Backup the current state of a WLED device or multiple discovered devices. | ||
update Update the firmware of a WLED device or multiple discovered devices. | ||
discover Discover WLED devices using mDNS and list their IP addresses and names. | ||
|
||
Examples: | ||
# Discover all WLED devices on the network | ||
./wled-tools discover | ||
|
||
# Backup a specific WLED device | ||
./wled-tools -t 192.168.1.100 backup | ||
|
||
# Backup all discovered WLED devices to a specific directory | ||
./wled-tools -D -d /path/to/backups backup | ||
|
||
# Update firmware on all discovered WLED devices | ||
./wled-tools -D -f /path/to/firmware.bin update | ||
|
||
EOF | ||
} | ||
|
||
# Discover devices using mDNS | ||
discover_devices() { | ||
if ! command -v avahi-browse &> /dev/null; then | ||
log "ERROR" "$RED" "'avahi-browse' is required but not installed, please install avahi-utils using your preferred package manager." | ||
exit 1 | ||
fi | ||
|
||
# Map avahi responses to strings seperated by 0x1F (unit separator) | ||
mapfile -t raw_devices < <(avahi-browse _wled._tcp --terminate -r -p | awk -F';' '/^=/ {print $7"\x1F"$8"\x1F"$9}') | ||
|
||
local devices_array=() | ||
for device in "${raw_devices[@]}"; do | ||
IFS=$'\x1F' read -r hostname address port <<< "$device" | ||
devices_array+=("$hostname" "$address" "$port") | ||
done | ||
|
||
echo "${devices_array[@]}" | ||
} | ||
|
||
# Backup one device | ||
backup_one() { | ||
local hostname="$1" | ||
local address="$2" | ||
local port="$3" | ||
|
||
log "INFO" "$YELLOW" "Backing up device config/presets: $hostname ($address:$port)" | ||
|
||
mkdir -p "$backup_dir" | ||
|
||
local cfg_url="http://$address:$port/cfg.json" | ||
local presets_url="http://$address:$port/presets.json" | ||
local cfg_dest="${backup_dir}/${hostname}.cfg.json" | ||
local presets_dest="${backup_dir}/${hostname}.presets.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"" | ||
|
||
if ! curl_handler "$curl_command_cfg" "$hostname"; 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 | ||
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" | ||
log "INFO" "$GREEN" "Successfully backed up config and presets for $hostname" | ||
return 0 | ||
} | ||
|
||
# Update one device | ||
update_one() { | ||
local hostname="$1" | ||
local address="$2" | ||
local port="$3" | ||
local firmware="$4" | ||
|
||
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 | ||
log "ERROR" "$RED" "Failed to update firmware for $hostname" | ||
return 1 | ||
fi | ||
|
||
log "INFO" "$GREEN" "Successfully initiated firmware update for $hostname" | ||
return 0 | ||
} | ||
|
||
# Command-line arguments processing | ||
command="" | ||
target="" | ||
discover=false | ||
quiet=false | ||
backup_dir="./" | ||
firmware_file="" | ||
|
||
if [ $# -eq 0 ]; then | ||
show_help | ||
exit 0 | ||
fi | ||
|
||
while [[ $# -gt 0 ]]; do | ||
case "$1" in | ||
-h|--help) | ||
show_help | ||
exit 0 | ||
;; | ||
-t|--target) | ||
if [ -z "$2" ] || [[ "$2" == -* ]]; then | ||
log "ERROR" "$RED" "The --target option requires an argument." | ||
exit 1 | ||
fi | ||
target="$2" | ||
shift 2 | ||
;; | ||
-D|--discover) | ||
discover=true | ||
shift | ||
;; | ||
-d|--directory) | ||
if [ -z "$2" ] || [[ "$2" == -* ]]; then | ||
log "ERROR" "$RED" "The --directory option requires an argument." | ||
exit 1 | ||
fi | ||
backup_dir="$2" | ||
shift 2 | ||
;; | ||
-f|--firmware) | ||
if [ -z "$2" ] || [[ "$2" == -* ]]; then | ||
log "ERROR" "$RED" "The --firmware option requires an argument." | ||
exit 1 | ||
fi | ||
firmware_file="$2" | ||
shift 2 | ||
;; | ||
-q|--quiet) | ||
quiet=true | ||
shift | ||
;; | ||
backup|update|discover) | ||
command="$1" | ||
shift | ||
;; | ||
*) | ||
log "ERROR" "$RED" "Unknown argument: $1" | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
# Execute the appropriate command | ||
case "$command" in | ||
discover) | ||
read -ra devices <<< "$(discover_devices)" | ||
for ((i=0; i<${#devices[@]}; i+=3)); do | ||
hostname="${devices[$i]}" | ||
address="${devices[$i+1]}" | ||
port="${devices[$i+2]}" | ||
|
||
if [ "$quiet" = true ]; then | ||
echo "$hostname" | ||
else | ||
log "INFO" "$BLUE" "Discovered device: Hostname=$hostname, Address=$address, Port=$port" | ||
fi | ||
done | ||
;; | ||
backup) | ||
if [ -n "$target" ]; then | ||
# Assume target is both the hostname and address, with port 80 | ||
backup_one "$target" "$target" "80" | ||
elif [ "$discover" = true ]; then | ||
read -ra devices <<< "$(discover_devices)" | ||
for ((i=0; i<${#devices[@]}; i+=3)); do | ||
hostname="${devices[$i]}" | ||
address="${devices[$i+1]}" | ||
port="${devices[$i+2]}" | ||
backup_one "$hostname" "$address" "$port" | ||
done | ||
else | ||
log "ERROR" "$RED" "No target specified. Use --target or --discover." | ||
exit 1 | ||
fi | ||
;; | ||
update) | ||
# Validate firmware before proceeding | ||
if [ -z "$firmware_file" ] || [ ! -f "$firmware_file" ]; then | ||
log "ERROR" "$RED" "Please provide a file in --firmware that exists" | ||
exit 1 | ||
fi | ||
|
||
if [ -n "$target" ]; then | ||
# Assume target is both the hostname and address, with port 80 | ||
update_one "$target" "$target" "80" "$firmware_file" | ||
elif [ "$discover" = true ]; then | ||
LordMike marked this conversation as resolved.
Show resolved
Hide resolved
|
||
read -ra devices <<< "$(discover_devices)" | ||
for ((i=0; i<${#devices[@]}; i+=3)); do | ||
hostname="${devices[$i]}" | ||
address="${devices[$i+1]}" | ||
port="${devices[$i+2]}" | ||
update_one "$hostname" "$address" "$port" "$firmware_file" | ||
done | ||
else | ||
log "ERROR" "$RED" "No target specified. Use --target or --discover." | ||
exit 1 | ||
fi | ||
;; | ||
*) | ||
show_help | ||
exit 1 | ||
;; | ||
esac |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.