Hi everyone!
Inspired by the flashlight feature on Smartwatches (Apple/Garmin/Samsung/Google), I had an idea for the upcoming spruceOS 4.2.1 Quick Bar. Since the TrimUI Smart Pro S has a very bright IPS screen and those great RGB rings, why not use them as a quick flashlight?
I wrote a small, clean toggle script that saves your current brightness, blasts everything to Max White, and restores your settings on the second click. It would be an amazing "Quality of Life" addition to the top bar toggles!
Here is the code:
bash
#!/bin/sh
# ---------------------------------------------------------
# Smart Flashlight Toggle v2.7.5
# Target: TrimUI Smart Pro S / spruceOS 4.1.2+
#
# Features:
# - Quick Bar friendly on/off/long/toggle/status
# - Atomic lock via directory (concurrency safe)
# - Saves and restores exact previous hardware states
# - Integer validation for stored values
# - Safe thermal management (fan control)
# - Optional amber marker file for external script sync
# ---------------------------------------------------------
# Paths for persistence and locking
BASE_DIR="/mnt/SDCARD/App/Flashlight"
STATE_FILE="$BASE_DIR/state.env"
LOCKDIR="/tmp/flashlight.lock"
AMBER_FILTER="/tmp/amber_active"
# Hardware Control Nodes
FAN_CTRL="/sys/class/hwmon/hwmon0/pwm1"
LED_CTRL="/sys/class/leds/led_ring/brightness"
DISPLAY_BACKLIGHT="/sys/class/backlight/backlight/brightness"
# --- Utility Functions ---
# Check if a hardware node exists and is writable
node_writable() {
[ -e "$1" ] && [ -w "$1" ]
}
# Read hardware node value safely
read_node() {
[ -r "$1" ] && cat "$1" 2>/dev/null
}
# Write value to hardware node safely
write_node() {
node="$1"
value="$2"
node_writable "$node" && printf '%s\n' "$value" > "$node" 2>/dev/null
}
# Ensure input is a positive integer, otherwise use default
read_int_or_default() {
value="$1"
default="$2"
case "$value" in
''|*[!0-9]*) echo "$default" ;;
*) [ "$value" -lt 0 ] && echo "$default" || echo "$value" ;;
esac
}
# Ensure integer does not exceed hardware limits
read_capped_int() {
value="$1"
cap="$2"
case "$value" in
''|*[!0-9]*) echo "$cap" ;;
*) [ "$value" -gt "$cap" ] && echo "$cap" || echo "$value" ;;
esac
}
# Verify required hardware nodes and directories
check_env() {
[ -d "$BASE_DIR" ] || {
echo "Missing required directory: $BASE_DIR" >&2
exit 1
}
for p in "$DISPLAY_BACKLIGHT" "$FAN_CTRL" "$LED_CTRL"; do
[ -e "$p" ] || {
echo "Missing required path: $p" >&2
exit 1
}
done
}
# Use atomic directory creation to prevent multiple instances
acquire_lock() {
if ! mkdir "$LOCKDIR" 2>/dev/null; then
echo "Flashlight already active or lock exists ($LOCKDIR)." >&2
exit 0
fi
}
# --- State Management ---
# Backup current hardware settings before activation
save_state() {
mkdir -p "$BASE_DIR" 2>/dev/null || return 1
prev_backlight="$(read_int_or_default "$(read_node "$DISPLAY_BACKLIGHT")" 100)"
prev_backlight="$(read_capped_int "$prev_backlight" 255)"
prev_led="$(read_int_or_default "$(read_node "$LED_CTRL")" 0)"
prev_led="$(read_capped_int "$prev_led" 255)"
prev_fan="$(read_int_or_default "$(read_node "$FAN_CTRL")" 0)"
prev_fan="$(read_capped_int "$prev_fan" 255)"
# Export states to an environment file
{
echo "PREVIOUS_BRIGHTNESS=$prev_backlight"
echo "PREVIOUS_LED=$prev_led"
echo "PREVIOUS_FAN=$prev_fan"
} > "$STATE_FILE" 2>/dev/null
}
# Load backed-up states into variables
load_state() {
PREVIOUS_BRIGHTNESS=""
PREVIOUS_LED=""
PREVIOUS_FAN=""
if [ -r "$STATE_FILE" ]; then
. "$STATE_FILE" 2>/dev/null
# Sanitize loaded values
PREVIOUS_BRIGHTNESS="$(read_capped_int "$(read_int_or_default "$PREVIOUS_BRIGHTNESS" 100)" 255)"
PREVIOUS_LED="$(read_capped_int "$(read_int_or_default "$PREVIOUS_LED" 0)" 255)"
PREVIOUS_FAN="$(read_capped_int "$(read_int_or_default "$PREVIOUS_FAN" 0)" 255)"
fi
}
# Handle amber filter signaling (marker file)
set_mode() {
case "$1" in
long) touch "$AMBER_FILTER" ;;
*) rm -f "$AMBER_FILTER" 2>/dev/null ;;
esac
}
# Get target brightness from config file or default to max
load_level() {
if [ -r "$BASE_DIR/last_level.txt" ]; then
saved="$(read_node "$BASE_DIR/last_level.txt")"
read_capped_int "$(read_int_or_default "$saved" 255)" 255
else
echo 255
fi
}
# Apply hardware changes for flashlight mode
apply_active_state() {
# Boost fan speed for thermal safety (PWM 150)
write_node "$FAN_CTRL" 150
write_node "$LED_CTRL" "$ACTIVE_LED"
write_node "$DISPLAY_BACKLIGHT" "$ACTIVE_BRIGHTNESS"
set_mode "$1"
}
# Restore original hardware state and remove lock
cleanup() {
if [ -r "$STATE_FILE" ]; then
load_state
fi
# Gracefully restore LED, Fan, and Backlight
if [ -n "$PREVIOUS_LED" ]; then
write_node "$LED_CTRL" "$PREVIOUS_LED"
else
write_node "$LED_CTRL" 0
fi
if [ -n "$PREVIOUS_FAN" ]; then
write_node "$FAN_CTRL" "$PREVIOUS_FAN"
else
write_node "$FAN_CTRL" 0
fi
if [ -n "$PREVIOUS_BRIGHTNESS" ]; then
write_node "$DISPLAY_BACKLIGHT" "$PREVIOUS_BRIGHTNESS"
fi
# Cleanup temporary files and locks
rm -f "$AMBER_FILTER" 2>/dev/null
rm -f "$STATE_FILE" 2>/dev/null
rmdir "$LOCKDIR" 2>/dev/null
}
# --- Main Logic ---
main_on() {
save_state || exit 1
load_state
ACTIVE_LED="$(load_level)"
ACTIVE_BRIGHTNESS="$ACTIVE_LED"
apply_active_state "$1"
# Ensure cleanup runs on exit
trap cleanup EXIT INT TERM
}
main_off() {
if [ -d "$LOCKDIR" ] || [ -r "$STATE_FILE" ]; then
cleanup
fi
}
main_status() {
if [ -d "$LOCKDIR" ]; then
echo "Smart Flashlight: ACTIVE"
else
echo "Smart Flashlight: INACTIVE"
fi
}
# --- Command Router ---
case "$1" in
on)
check_env
acquire_lock
main_on white
;;
long)
check_env
acquire_lock
main_on long
;;
off)
main_off
;;
toggle)
if [ -d "$LOCKDIR" ]; then
main_off
else
"$0" on
fi
;;
status)
main_status
;;
*)
echo "Usage: $0 {on|long|off|toggle|status}" >&2
exit 1
;;
esac
exit 0
Hi everyone!
Inspired by the flashlight feature on Smartwatches (Apple/Garmin/Samsung/Google), I had an idea for the upcoming spruceOS 4.2.1 Quick Bar. Since the TrimUI Smart Pro S has a very bright IPS screen and those great RGB rings, why not use them as a quick flashlight?
I wrote a small, clean toggle script that saves your current brightness, blasts everything to Max White, and restores your settings on the second click. It would be an amazing "Quality of Life" addition to the top bar toggles!
Here is the code: