Skip to content

Instantly share code, notes, and snippets.

@BOSSoNe0013
Last active January 16, 2026 13:46
Show Gist options
  • Select an option

  • Save BOSSoNe0013/94e828fa8d9ff4c1a455c13e6c6b76e7 to your computer and use it in GitHub Desktop.

Select an option

Save BOSSoNe0013/94e828fa8d9ff4c1a455c13e6c6b76e7 to your computer and use it in GitHub Desktop.
Watch screen status on GNU/Linux and change devices lightning modes/colors accordingly
#!/bin/bash
# _____ _____ _ _ _ _ _ _
# / ___| / ___| | | | | | | | | | | |
# \ `--. ___ _ __ ___ ___ _ __ \ `--.| |_ __ _| |_ _ _ ___ | | | | __ _| |_ ___| |__ ___ _ __
# `--. \/ __| '__/ _ \/ _ \ '_ \ `--. \ __/ _` | __| | | / __| | |/\| |/ _` | __/ __| '_ \ / _ \ '__|
# /\__/ / (__| | | __/ __/ | | | /\__/ / || (_| | |_| |_| \__ \ \ /\ / (_| | || (__| | | | __/ |
# \____/ \___|_| \___|\___|_| |_| \____/ \__\__,_|\__|\__,_|___/ \/ \/ \__,_|\__\___|_| |_|\___|_|
#
# Copyright (C) 2026 Cyril Bosselut
#
# WatchScreenStatus is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# WatchScreenStatus is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with WatchScreenStatus If not, see <http://www.gnu.org/licenses/>.
PID="$$"
v_major=1
v_minor=1
v_patch=0
VERSION="$v_major.$v_minor.$v_patch"
i=1
sp="/-\|"
DAEMON=false
SHOW_VERSION=false
MEMORY_ANIM_RUNNING=false
mkdir /tmp/wss 2>/dev/null
wss_tmp_file=$(mktemp -p /tmp/wss wss.XXXXX)
state_tmp_file=$(mktemp -p /tmp/wss state.XXXXX)
anim_tmp_file=$(mktemp -p /tmp/wss anim.XXXXX)
RED="FF0000"
GREEN="00FF00"
BLUE="0000FF"
YELLOW="FFFF00"
PINK="FF06B5"
CYAN="00FFFF"
ORANGE="FF2000"
AMBER="FF0400"
GOLD="FF4000"
GPU_MODE_DIRECT="Direct"
GPU_MODE_COLOR_SHIFT="Color Shift"
DEFAULT_MOUSE_COLOR=$ORANGE
DEFAULT_KEYBOARD_COLOR=$ORANGE
DEFAULT_MACRO_KEYBOARD_COLOR=$ORANGE
DEFAULT_GPU_COLOR=$AMBER
DEFAULT_GPU_MODE=$GPU_MODE_DIRECT
DEFAULT_MOTHERBOARD_COLOR=$ORANGE
DEFAULT_MEMORY_COLOR=$ORANGE
DEFAULT_PUMP_COLOR=$ORANGE
DEFAULT_CASE_COLOR="$ORANGE,$ORANGE"
####################################
# Lian Li hub leds per channel
# Port 1 : 20
# Port 2 : 80
# Port 3 : 60
# Port 4 : 80
####################################
MOUSE_COLOR=$DEFAULT_MOUSE_COLOR
KEYBOARD_COLOR=$DEFAULT_KEYBOARD_COLOR
MACRO_KEYBOARD_COLOR=$DEFAULT_KEYBOARD_COLOR
GPU_COLOR=$DEFAULT_GPU_COLOR
GPU_MODE=$DEFAULT_GPU_MODE
MOTHERBOARD_COLOR=$DEFAULT_MOTHERBOARD_COLOR
MEMORY_COLOR_1=$DEFAULT_MEMORY_COLOR
MEMORY_COLOR_2=$DEFAULT_MEMORY_COLOR
CASE_COLOR=$DEFAULT_CASE_COLOR
PUMP_COLOR=$DEFAULT_PUMP_COLOR
STANDBY_COLOR=$RED
PREVIOUS_SCREEN_STATE="unknown"
WSS_RUNNING=true
CASE_NAME="Lian Li Uni Hub - AL"
GPU_NAME="Gigabyte AORUS GeForce RTX 4080 MASTER"
MOTHERBOARD_NAME="MSI MEG Z490 UNIFY (MS-7C71)"
MEMORY_NAME="HyperX Predator RGB"
KEYBOARD_NAME="Razer BlackWidow Chroma V2"
MACRO_KEYBOARD_NAME="Macro Keyboard"
MOUSE_NAME="Roccat Burst Pro"
MEMORY_ZONES=("HyperX Slot 1" "HyperX Slot 2" "HyperX Slot 3" "HyperX Slot 4")
SCREEN_STATE="unknown"
STATE="offline"
MQTT_TIMER=0
export SCREEN_STATE
export MEMORY_ANIM_RUNNING
export WSS_RUNNING
function print_help {
echo "This script watch the DPMS screen status and update computer lights colors."
echo "The state is also sent to a MQTT node".
echo
echo "Syntax:"
echo " watch_screen_status.sh [-d|h|v]"
echo "options:"
echo " -d Run as a daemon."
echo " -h Print this Help."
echo " -v Print software version and exit."
echo
}
while getopts ":dvh" option; do
case $option in
d) #daemon
DAEMON=true;;
v) #display version
echo $VERSION
exit;;
h) #display help
print_help
exit;;
esac
done
function mqtt_pub {
wss_log "Send status ($STATE) to mqtt broker"
mosquitto_pub -r -t TheHost/status -m "$1" &
}
function onexit {
if [[ $WSS_RUNNING == true ]]; then
wss_log "Stopping WSS"
WSS_RUNNING=false
echo false > "$wss_tmp_file"
updateColors
rm -f "$anim_tmp_file"
rm -f "$state_tmp_file"
rm -f "$wss_tmp_file"
lockfile-remove $LOCK_FILE
fi
exit
}
function wss_log {
now=$(date +"%x %T")
if [[ $DAEMON == false ]]; then
echo "$now - $1" 1>&2
fi
logger --id="$PID" -t "WSS" "$1"
}
function setStaticColors {
openrgb --loglevel 0 --nodetect \
-d "$GPU_NAME" -m "$GPU_MODE" -c $GPU_COLOR -b 100 -s 100 \
-d "$CASE_NAME" -m Runway -c "$CASE_COLOR" -b 100 \
-d "$MOTHERBOARD_NAME" -m Static -c $MOTHERBOARD_COLOR -b 100 \
-d "$MOTHERBOARD_NAME" -z 2 -c $PUMP_COLOR -b 100 \
-d "$MEMORY_NAME" -m Direct -c $MEMORY_COLOR_1 -b 100 \
-d "$MOUSE_NAME" -m Direct -c $MOUSE_COLOR -b 100 \
-d "$KEYBOARD_NAME" -m Direct -c $KEYBOARD_COLOR -b 100 \
-d "$MACRO_KEYBOARD_NAME" -m Direct -c $MACRO_KEYBOARD_COLOR -b 100 1>&2 > /dev/null
rm -f ~/.config/OpenRGB/logs/*.log
}
function animateMemory {
i=0
d=1
MEMORY_ANIM_RUNNING=$(cat $anim_tmp_file)
if [[ $MEMORY_ANIM_RUNNING == true ]]; then
wss_log "Memory animation already running"
return
fi
MEMORY_ANIM_RUNNING=true
echo true > "$anim_tmp_file"
wss_log "Starting memory animation"
while [ $SCREEN_STATE == 'on' ]; do
WSS_RUNNING=$(cat $wss_tmp_file)
if [[ $WSS_RUNNING == false ]]; then
MEMORY_ANIM_RUNNING=false
echo false > "$anim_tmp_file"
wss_log "Stopping memory animation"
break
fi
SCREEN_STATE=$(cat $state_tmp_file)
if [[ $SCREEN_STATE == 'on' ]]; then
n=$((i % 4))
z=0
if [[ $n == 1 ]]; then
z=2
elif [[ $n == 2 ]]; then
z=1
else
z=$n
fi
openrgb --loglevel 0 --nodetect \
-d "$MEMORY_NAME" -m Direct -c $MEMORY_COLOR_1 -b 100 \
-d "$MEMORY_NAME" -z $z -c $MEMORY_COLOR_2 -b 100 1>&2 > /dev/null
i=$((i + d))
if [[ $i == 3 ]]; then
d=-1
elif [[ $i == 0 ]]; then
d=1
fi
fi
rm -f ~/.config/OpenRGB/logs/*.log
sleep 0.5
done
MEMORY_ANIM_RUNNING=false
echo false > "$anim_tmp_file"
wss_log "Memory animation stopped"
}
function getStandbyColor {
if [[ -n ${CUSTOM_STANDBY_COLOR} ]]; then
echo $CUSTOM_STANDBY_COLOR
else
echo $STANDBY_COLOR
fi
}
function setNeoColor {
if [[ $STATE == "asleep" ]]; then
curl -k -s -f https://neo.b1project.com:5000/devices/31/set/lights -d "method=light_color&room=bureau&device=31&args=%7B%22target%22%3A-1%2C+%22color%22%3A%22255,0,0%22%7D" 1>&2 > /dev/null
elif [[ $STATE == "awake" ]]; then
curl -k -s -f https://neo.b1project.com:5000/devices/31/set/lights -d "method=light_color&room=bureau&device=31&args=%7B%22target%22%3A-1%2C+%22color%22%3A%220,0,255%22%7D" 1>&2 > /dev/null
else
curl -k -s -f https://neo.b1project.com:5000/devices/31/set/lights -d "method=light_off&room=bureau&device=31&args=%7B%22target%22%3A-1%2C+%22color%22%3A%22255,0,0%22%7D" 1>&2 > /dev/null
fi
}
function updateColors {
MOUSE_COLOR=$DEFAULT_MOUSE_COLOR
KEYBOARD_COLOR=$DEFAULT_KEYBOARD_COLOR
MACRO_KEYBOARD_COLOR=$DEFAULT_MACRO_KEYBOARD_COLOR
GPU_COLOR=$DEFAULT_GPU_COLOR
GPU_MODE=$DEFAULT_GPU_MODE
MOTHERBOARD_COLOR=$DEFAULT_MOTHERBOARD_COLOR
MEMORY_COLOR_1=$DEFAULT_MEMORY_COLOR
MEMORY_COLOR_2=$DEFAULT_MEMORY_COLOR
PUMP_COLOR=$DEFAULT_PUMP_COLOR
CASE_COLOR=$DEFAULT_CASE_COLOR
if [[ $WSS_RUNNING == false ]]; then
wss_log "Mouse & keyboard switch to default color"
STATE="offline"
setStaticColors
setNeoColor
elif [[ $SCREEN_STATE != "on" ]]; then
color=`getStandbyColor`
wss_log "Standby color: $color"
KEYBOARD_COLOR=$color
MACRO_KEYBOARD_COLOR=$color
MOUSE_COLOR=$color
GPU_COLOR=$color
MOTHERBOARD_COLOR=$color
MEMORY_COLOR_1=$color
MEMORY_COLOR_2=$color
PUMP_COLOR=$color
CASE_COLOR="$color,$color"
STATE="asleep"
wss_log "Mouse, keyboard, case and GPU switch to standby color"
setStaticColors &
setNeoColor &
else
wss_log "Mouse, case and GPU switch to MIAMI preset"
KEYBOARD_COLOR=$BLUE
MACRO_KEYBOARD_COLOR=$BLUE
MOUSE_COLOR="$PINK,$BLUE"
GPU_COLOR="$PINK,$BLUE,$BLUE"
GPU_MODE=$GPU_MODE_COLOR_SHIFT
MOTHERBOARD_COLOR=$BLUE
MEMORY_COLOR_1=$BLUE
MEMORY_COLOR_2=$PINK
PUMP_COLOR=$PINK
CASE_COLOR="$PINK,$BLUE"
STATE="awake"
setStaticColors &
setNeoColor &
sleep 3
wss_log "Keyboard switch to RAINBOW preset"
polychromatic-cli -e Rainbow2 > /dev/null
animateMemory &
fi
}
function checkDPMSStatus {
DPMS_STATE=`xset q | grep 'DPMS is ' | cut -d' ' -f5| tr '[:upper:]' '[:lower:]'`
if [[ $DPMS_STATE == "disabled" ]]; then
wss_log "Enabling DPMS"
if [[ $DAEMON == false ]]; then
echo "" 1>&2
fi
xset +dpms
fi
}
function checkScreenStatus {
if [[ $MQTT_TIMER == 30 ]]; then
MQTT_TIMER=0
fi
#SCREEN_STATE=`xset q | grep 'Monitor is ' | cut -d' ' -f6| tr '[:upper:]' '[:lower:]'`
SCREEN_STATE=`kscreen-doctor --dpms show|cut -d' ' -f6`
if [[ $SCREEN_STATE == "" ]]; then
sleep 0.5
SCREEN_STATE=`xset q | grep 'Monitor is ' | cut -d' ' -f5| tr '[:upper:]' '[:lower:]'`
if [[ $SCREEN_STATE == "" ]]; then
wss_log "Can't check screen state"
return 0
fi
fi
echo $SCREEN_STATE > "$state_tmp_file"
if [[ $SCREEN_STATE != $PREVIOUS_SCREEN_STATE ]]; then
wss_log "Screen state changed from ${PREVIOUS_SCREEN_STATE} to ${SCREEN_STATE}"
PREVIOUS_SCREEN_STATE=$SCREEN_STATE
updateColors
if [[ $DAEMON == false ]]; then
echo "" 1>&2
fi
MQTT_TIMER=0
elif [[ $DAEMON == false ]]; then
echo -n -e "\b${sp:i++%${#sp}:1}" 1>&2
fi
if [[ $MQTT_TIMER == 0 ]]; then
mqtt_pub "$STATE" &
fi
MQTT_TIMER=$((++MQTT_TIMER))
}
function main {
if [[ $DAEMON == false ]]; then
echo "###########################################################################################################" 1>&2
echo "# _____ _____ _ _ _ _ _ _ #" 1>&2
echo "# / ___| / ___| | | | | | | | | | | | #" 1>&2
echo "# \ \`--. ___ _ __ ___ ___ _ __ \ \`--.| |_ __ _| |_ _ _ ___ | | | | __ _| |_ ___| |__ ___ _ __ #" 1>&2
echo "# \`--. \/ __| '__/ _ \/ _ \ '_ \ \`--. \ __/ _\` | __| | | / __| | |/\| |/ _\` | __/ __| '_ \ / _ \ '__| #" 1>&2
echo "# /\__/ / (__| | | __/ __/ | | | /\__/ / || (_| | |_| |_| \__ \ \ /\ / (_| | || (__| | | | __/ | #" 1>&2
echo "# \____/ \___|_| \___|\___|_| |_| \____/ \__\__,_|\__|\__,_|___/ \/ \/ \__,_|\__\___|_| |_|\___|_| #" 1>&2
echo "# #" 1>&2
echo "###########################################################################################################" 1>&2
echo "Ver: $VERSION" 1>&2
echo "" 1>&2
fi
wss_log "Starting screen status watcher with PID: $PID"
echo true > "$wss_tmp_file"
xbindkeys_autostart
mqtt_pub loading &
while $WSS_RUNNING == true; do
checkDPMSStatus
checkScreenStatus
sleep 1
done
mqtt_pub offline &
WSS_RUNNING=false
echo false > "$wss_tmp_file"
updateColors
wss_log "Closing screen status watcher"
rm -f "$anim_tmp_file"
rm -f "$state_tmp_file"
rm -f "$wss_tmp_file"
}
LOCK_FILE=/tmp/watch_screen_status-$UID
lockfile-create --retry 0 --use-pid $LOCK_FILE || exit
trap onexit EXIT INT TERM KILL QUIT HUP
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment