Skip to content

Instantly share code, notes, and snippets.

@mininmobile
Last active April 29, 2025 11:21
Show Gist options
  • Save mininmobile/e2c433eb39d3856d3268fb1c10aa7446 to your computer and use it in GitHub Desktop.
Save mininmobile/e2c433eb39d3856d3268fb1c10aa7446 to your computer and use it in GitHub Desktop.
#!/bin/ash
# usage: rainbowd [--help|-h|--version|-V|options...]
#
# --fork do not read or write pid file (FORK=true)
# --dump print initialized color buffer and exit (DUMP_FRAME=true)
# --verbose -v print color buffer table (DEBUG=true)
# --loop -L play animations, rotate by default (LOOP=true)
# --no-loop -l only render first frame (LOOP=false)
# --reverse -r reverse animations (REVERSE=true)
# --fade -F play fade animation (FADE=true)
# --blink -B play blink animation
#
# --dim(-[0123])? set led intesity (DIM_LED=[0123]) based on ambient light (DIM_LED=true)
# --no-dim -d do not set led intesity (DIM_LED=false)
# --invert invert the color buffer (INVERT=true)
# --brightness -b dim the color buffer (0-1.0)
# --timeout -t time in secs to wait before killing self
# --interval -i time in secs to wait between updates
#
# --nop -n do not reset color buffer
# --clear -C fill color buffer with "blank" color
# --color -c fill color buffer with color
# --bg fill "blank" color buffer with color
# --spinner -s append color to hind of color buffer
# --frame -f set color buffer (12 hex colors with no spaces)
# --pastel use pastel rainbow color palette
#
# misc env vars: IO_RING IO_AMBIENT IO_LED_DIM
#
# rainbow_daemon.sh v0.9
# [email protected]
# default pattern (12 hex colors)
frame='0000ff0000ff4f00cf4f00cfff0000ff0000af2f00af2f00ffff00ffff0000ff0000ff00'
# misc defaults
[ -z "$DEBUG" ] && DEBUG=false
[ -z "$DUMP_FRAME" ] && DUMP_FRAME=false
[ -z "$LOOP" ] && LOOP=true
[ -z "$DIM_LED" ] && DIM_LED=true
brightness='1'
interval='0.1'
blank_frame='000000000000000000000000000000000000000000000000000000000000000000000000'
# devices: led array; ambient light sensor; led power
[ -z "$IO_RING" ] && IO_RING='/sys/bus/i2c/devices/0-003f/frame'
[ -z "$IO_AMBIENT" ] && IO_AMBIENT='/sys/bus/iio/devices/iio:device0/calibrated_lux'
[ -z "$IO_LED_DIM" ] && IO_LED_DIM='/sys/bus/i2c/devices/0-003f/led_current'
while [ $# -gt 0 ]; do case $1 in
"--help" | "-h") sed -n "/^# usage/,/^# ~/p" "$(which $0)" | sed -E 's/(# |#)//'; exit ;;
"--version" | "-V") grep "rainbow_daemon.sh\sv" "$(which $0)" | sed -E 's/(# |#)//'; exit ;;
"--fork" ) FORK=true; shift ;;
"--dump" ) DUMP_FRAME=true; shift ;;
"--verbose" | "-v") DEBUG=true; shift ;;
"--loop" | "-L") LOOP=true; shift ;;
"--no-loop" | "-l") LOOP=false; shift ;;
"--reverse" | "-r") REVERSE=true; shift ;;
"--blink" | "-B") blink=true; shift ;;
"--fade" | "-F") FADE=true; shift ;;
"--dim" ) DIM_LED=true; shift ;; "--dim-0") DIM_LED=0; shift ;; "--dim-1") DIM_LED=1; shift ;; "--dim-2") DIM_LED=2; shift ;; "--dim-3") DIM_LED=3; shift ;;
"--no-dim" | "-d") DIM_LED=false; shift ;;
"--invert" ) INVERT=true; shift ;;
"--brightness" | "-b") brightness=$2; shift 2 ;;
"--interval" | "-i") interval=$2; shift 2 ;;
"--timeout" | "-t") timeout=$2; shift 2 ;;
"--nop" | "-n") LOOP=false; frame=$(cat /sys/bus/i2c/devices/0-003f/frame); blank_frame=$frame; shift ;;
"--clear" | "-C") LOOP=false; blink=false; frame=$blank_frame; shift ;;
"--color" | "-c") LOOP=false; frame=$(seq 12 | awk "{printf \"$2\"}"); shift 2 ;;
"--bg" ) blank_frame=$(seq 12 | awk "{printf \"$2\"}"); shift 2;;
"--spinner" | "-s") LOOP=true; frame=$2$blank_frame; shift 2 ;;
"--frame" | "-f") frame=$2; shift 2 ;;
"--pastel") frame='881177aa3355cc6666ee9944eedd0099dd5544dd8822ccbb00bbcc0099cc3366bb663399'; blank_frame=$frame; shift ;;
*) echo "unknown argument: $1"; exit ;;
esac; done
# helper functions
transcolor() {
r=$(printf "%02x" $(printf "($2)/1" 0x$(echo $1 | cut -c 1-2) | bc))
g=$(printf "%02x" $(printf "($2)/1" 0x$(echo $1 | cut -c 3-4) | bc))
b=$(printf "%02x" $(printf "($2)/1" 0x$(echo $1 | cut -c 5-6) | bc))
echo -n "$r$g$b"
}
darken() { transcolor $1 "%d*$2"; }
invert() { transcolor $1 "255-%d"; }
invertframe() { for i in $(seq 1 12); do echo -n $(invert $(echo $1 | cut -c $(( i*6 - 5 ))-$(( i*6 ))) $2); done; }
darkenframe() { for i in $(seq 1 12); do echo -n $(darken $(echo $1 | cut -c $(( i*6 - 5 ))-$(( i*6 ))) $2); done; }
# debug functions
printc() {
r=$(printf "%d" 0x$(echo $1 | cut -c 1-2))
g=$(printf "%d" 0x$(echo $1 | cut -c 3-4))
b=$(printf "%d" 0x$(echo $1 | cut -c 5-6))
echo -n "\033[48;2;${r};${g};${b}m    \033[0m"
}
printframe() {
for i in $(seq 1 12); do echo -n $(printc $(echo $1 | cut -c $(( i*6 - 5 ))-$(( i*6 )))); done
echo -n "\033[0m"
}
# init rainbowd
# * get initial color buffer
frame=$(echo $frame | cut -c -72)
[ "$DEBUG" = true ] && echo -e "< $(printframe $frame)" &
# → apply invert filer
[ "$INVERT" = true ] && { frame=$(invertframe $frame)
[ "$DEBUG" = true ] && echo -e "* $(printframe $frame)" &
} # → apply darken filer
[ ! "$brightness" = 1 ] && { frame=$(darkenframe $frame $brightness)
[ "$DEBUG" = true ] && echo -e "* $(printframe $frame)" &
}
[ "$DUMP_FRAME" = true ] && { echo "$frame"; exit; }
# * replace previous instance of rainbowd
if [ -z "$FORK" ]; then
if [ -e /run/rainbow_daemon.pid ] && [ -d /proc/$(cat /run/rainbow_daemon.pid) ]; then
kill -9 $(cat /run/rainbow_daemon.pid)
fi
echo $$ > /run/rainbow_daemon.pid
fi
# * set timeout
if [ -n "$timeout" ]; then
{ sleep $timeout; kill -9 $$; rainbowd --fork --clear; } &
fi
# * set led brightness
case "$DIM_LED" in
0|1|2|3) echo $DIM_LED > $IO_LED_DIM ;;
esac
# blink animation
if [ "$blink" = true ]; then while true; do
FORK=true DEBUG=$DEBUG LOOP=false DIM_LED=$DIM_LED INVERT=$INVERT rainbowd --frame $blank_frame
sleep $interval
FORK=true DEBUG=$DEBUG LOOP=false DIM_LED=$DIM_LED INVERT=$INVERT rainbowd --frame $frame
sleep $interval
done; exit; fi
# render
i=12
while true; do
if [ $i = 12 ] && [ "$DIM_LED" = true ]; then
ambient=$(cat $IO_AMBIENT)
if [ $ambient -gt 300 ]; then echo 0 > $IO_LED_DIM
elif [ $ambient -gt 200 ]; then echo 1 > $IO_LED_DIM
elif [ $ambient -gt 100 ]; then echo 2 > $IO_LED_DIM
else echo 3 > $IO_LED_DIM
fi
fi
i=$((i-1))
[ $i -le 1 ] && i=12
shift=000000
if [ "$LOOP" = true ]; then
if [ "$REVERSE" = true ]; then
shift=$(echo $frame | cut -c 67-72)
frame=$(echo "${shift}${frame}" | cut -c -72)
else
shift=$(echo $frame | cut -c 1-6)
frame=$(echo "${frame}${shift}" | cut -c 7-78)
fi
fi
if [ "$FADE" = true ]; then
[ ! "$LOOP" = true ] && { [ "$REVERSE" = true ] && shift=$(echo $frame | cut -c 67-72) || shift=$(echo $frame | cut -c 1-6); }
currentcolor=$(seq 12 | awk "{printf \"$shift\"}")
echo "$currentcolor" > $IO_RING
[ "$DEBUG" = true ] && echo -e "+ $(printc $shift) = $(printframe $currentcolor)" &
else
echo "$frame" > $IO_RING
[ "$DEBUG" = true ] && [ "$LOOP" = true ] && echo -e "+ $(printc $shift) = $(printframe $frame)" &
fi
[ ! "$LOOP" = true ] && exit
sleep $interval
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment