Hello my friends ๐
Today I edited one of my old scripts a bit so that I could post it on the website. I’ve written this script a long time ago, it calculates how long you will work with PC and reminds you after a certain amount of time that you need to take some rest(short break). This script automatically calculates the working time of the PC and the script does not take into account the times when you does not work with the PC(idle time). When you work with computers for a long time, this script is essential for your health.
As you know, there are many programs and device such as smart bands and smart watches to remind yourself of break times, but since I have a strong interest in creating personal code ๐ , I decided to write a small script for it; writing and creating personal tools is really fun for me ๐ . I’ve been using this script for a long time and it meets my needs and hopefully useful to you too. Studying this script can be useful for learning scripting in Linux as well, so I decided to publish this script.
If you just want to download and use the script click here
This script is writen in “Sublime”
In this script we will learn the following:
- How to read and save keyboards and mouse keys pressed(and mouse movements) by Bash script.
- How to check Bash script is executed with root privilege.
- How to detect Bash script is already running.
- How to play audio (with different frequencies) through speakers and built-in systems speaker(beep) in the Bash script.
- How to determine the idle time of user in the Bash script
- How to turn on and off the screen(monitor) by Bash script
- How to load variables from a file in Bash script.
- How to found name, ID and path of Bash script.
- How to access to user monitor(screen) by the root user or via ssh, find the keyboards event in /dev/input, get files size, do the decimal divides in the Bash scripts and etc.
Okay, let’s talk a little bit about the algorithm and what the script is going to do for us. What do we expect from the script? We want to set a specific working time and the script will alert us after the due date. We want to do this by turning off the monitor and playing the sound. Also let us know when the rest(short break) period is over by playing the sound and turning on the monitor. In addition, we want the script to only calculate the length of time we work with the system, and not to calculate idle time(If we have 2 minutes idle time, the script will allow us to work with the system for 2 minutes more than specified working time).So, the script algorithm is as follows:
- Wait for a minute.
- If the user was idle for one minute then add one minute to the user working deadline, otherwise add one minute to the user worked time.
- If the user was idle for the length of time he had set for rest, then set the worked time counter to zero (meaning the user has had enough rest and can work again as long as he has set).
- If this was the last minute of the working deadline, let the user know by playing audio. If the user requests more time, then add two minutes to the working deadline.
- If the working deadline has not expired, go to step 2 otherwise inform the user that the working deadline is over.
- Wait for the amount of rest time the user has set.
- Let the user know that the break is over and can work. Then go to step one.
Let’s start coding. Okay, first we define and quantify the variables we need:
#!/bin/bash
# Script name : RestTime
# Programmer : mazKnez
# Website : mazKnez.com
# 2019-08-17
########################### [-- Readme ################################
# This script has been tested on Debian-based Linux distributions.
# NOTICE : Check that these packages are installed on your system : " beep, pidof, speaker-test, pkill "
# NOTICE : config.cfg should be next to the script file.
# NOTICE : This script must be run with root access.
# NOTICE : If you want to run this script automatically (via service or in rc.local),
# you need to include in the file ".profile" the following commands to access the monitor:
# xhost +SI:localuser:root > /dev/null
# OR
# xhost + > /dev/null
#
#
# This script calculates how long you will work with PC and reminds you after a certain amount of time
# that you need to take some rest(short break).
# This script automatically calculates the working time of the PC and the script does not take into
# account the times when you does not work with the PC(idle time).
# When you work with computers for a long time, this script is essential for your health.
#
#
# Please read "README" file or visit www.mazKnez.com website for more information.
#
########################### Readme --] ################################
## [-- define consts
readonly script_path=$(dirname "${0}")"/"
readonly keyboards_file="${script_path}keyboards_dump" # The keys pressed from the keyboard
# are stored in this file.
readonly mice_file="${script_path}mice_dump" # Mouse movements and clicks
# are stored in this file.
readonly status_file="${script_path}status" # The script status is stored in this file,
# mostly used to verify the script.
readonly config_file="${script_path}config.cfg" # The script configuration is read from this file
export DISPLAY=:0
## define consts --]
It looks like the above code doesn’t need any explanation. In order to make the code readable and easy to code, I have tried to use functions as much as possible. Below we review these functions.
check_run_as_root () {
### This function checks script execution with super user
if ((UID != 0)); then # root UID == 0
echo "This script must run as root"
exit 1
fi
}
This function checks whether a user with root access has executed the script. Otherwise the script will not run.
detect_script_running () {
### This function checks whether another version of this script is running
### The best way to determine if software is running is to use a pidfile...
### But it is also applicable to this script.
script_name=$(basename "${0}")
is_another_script_running=$(pidof -x "${script_name}" -o $$)
# pidof checks if the program is running under this name
# Parameter -x was used to check the scripts.
# Parameter -o is used to delete the current execution of the script from the list.
if [ "${is_another_script_running}" ]; then
true
else
false
fi
}
This function checks whether the script is running or not. Returns “true” if the script was already executed. This function uses “pidof”. This method eliminates the need for us, but the best way to determine if the software is running is to use a pidfile so that every time the code is run, read the pid from the pidfile and check if pid is running, Then save the new pid code to the pidfile.
dump_keyboard_and_mouse () {
### This function stores keyboards and mice activity in the files.
### To detect user idle, we need to read the mouse and keyboard inputs.
### We use the method of directly reading files in "/dev/input".
### Along the way you can find the mice file and the keyboards file that starts with "event".
### "mice" contains mouse input data, but to find the file related to the keyboards
### we need to check the files in folder "by-path", keyboards file names end with "event-kbd".
### We need to find the links to the keyboards, and then we can find the keyboards event file
pkill --full "cat /dev/input/" # Kill previous execution of this function
kbdEvents=($(ls /dev/input/by-path | grep "event-kbd")) # Find files related to keyboards
for forCounter in "${kbdEvents[@]}"
do
eventFile=$(readlink --canonicalize "/dev/input/by-path/${forCounter}") # Get keyboards file location
cat "${eventFile}" > "${keyboards_file}" & # Store keyboards activity in the keyboards_file
done
cat /dev/input/mice > "${mice_file}" & # Store mice activity in the mice_file
}
This function stores keyboards and mice activity in the files. To detect user idle, we need to read the mouse and keyboard inputs. We use the method of directly reading files in “/dev/input”. Along the way you can find the mice file and the keyboards file that starts with “event”. The files in the path “/dev/input” will look like this:
drwxr-xr-x 2 root root 120 Aug 17 08:31 by-id
drwxr-xr-x 2 root root 140 Aug 17 08:32 by-path
crw-rw---- 1 root input 13, 64 Aug 17 08:31 event0
crw-rw---- 1 root input 13, 65 Aug 17 08:31 event1
crw-rw---- 1 root input 13, 74 Aug 17 08:31 event10
crw-rw---- 1 root input 13, 75 Aug 17 08:31 event11
crw-rw---- 1 root input 13, 76 Aug 17 08:31 event12
crw-rw---- 1 root input 13, 77 Aug 17 08:32 event13
crw-rw---- 1 root input 13, 66 Aug 17 08:31 event2
crw-rw---- 1 root input 13, 67 Aug 17 08:31 event3
crw-rw---- 1 root input 13, 68 Aug 17 08:31 event4
crw-rw---- 1 root input 13, 69 Aug 17 08:31 event5
crw-rw---- 1 root input 13, 70 Aug 17 08:31 event6
crw-rw---- 1 root input 13, 71 Aug 17 08:31 event7
crw-rw---- 1 root input 13, 72 Aug 17 08:31 event8
crw-rw---- 1 root input 13, 73 Aug 17 08:31 event9
crw-rw---- 1 root input 13, 63 Aug 17 08:31 mice
crw-rw---- 1 root input 13, 32 Aug 17 08:31 mouse0
“mice” contains mouse input data, but to find the file related to the keyboards we need to check the files in folder “by-path”, keyboards file names end with “event-kbd”. We need to find the links to the keyboards, and then we can find the keyboards event file. Folder “by-path” contents are similar to:
pci-0000:00:14.0-usb-0:3:1.0-event-mouse -> ../event3
pci-0000:00:14.0-usb-0:3:1.0-mouse -> ../mouse0
pci-0000:00:14.0-usb-0:4:1.0-event-kbd -> ../event4
pci-0000:00:14.0-usb-0:4:1.1-event -> ../event5
platform-pcspkr-event-spkr -> ../event13
Finally, we store the keyboard and mouse inputs in files “keyboards_dump” and “mice_dump” (with “cat” command).
load_variables () {
### This function loads variables from configuration file
### User can change the script behavior by these variables.
### NOTICE : This file(config.cfg) should be next to the script file.
if [ -f "${config_file}" ]; then # If config.cfg is extist
source "${config_file}"
else
workingTime=30 # In minutes
restingTime=5 # In minutes
idleDetection=true # If "true", the script also check idle time,
# for more information about it,
# read "is_user_working" function's comments
active=true # If "false", The script is temporarily disabled.
playSound=true # If "false", the script do tasks silently.
fi
}
This function loads variables from configuration file. A number of variables, such as working time and rest time, can be edited by the user through “config.cfg” file. This file should be placed next to the script file. To load the variables we use the “source” command.
is_last_minute () {
### This function checks whether this minute is the last minute of working time.
if ((whileCounter == workingTime-1)) && ((idleCounter == 0 )); then
true
else
false
fi
}
This function checks whether this minute is the last minute of working time. I wrote this condition to prevent script complexity and increase script readability in the function.
check_more_time_requested (){
### This function checks whether the user has pressed the ScrollLock key 3 or more times.
### The user can press more than 2 times the ScrollLock key, demanding more time to work.
scroll_lock_pressed=$(xxd "${keyboards_file}" | grep --count "0100 4600 0100 0000")
# xxd displays the input as hex.
# Hex code of ScrollLock in "keyboards_file" is "0100 4600 0100 0000"
if ((scroll_lock_pressed >= 3)); then
true # return true
else
false
fi
}
This function checks whether the user requests more time to work on the PC. If the user presses the key “Scroll-Lock” for 3 times or more, it means more time is required. To check if the key pressed, we need to check the file “keyboards-dump” and convert it to hex by using “xxd”.
play_sound () {
### This function plays sound through the speakers and the built-in speaker system.
### $1 == Frequency , $2 == Time in ms
if "${playSound}"; then # User can disable sound by changing the configuration file
second=$(bc --mathlib <<< "$2/1000")
# Decimal numbers cannot be calculated by bash.
# We have to use another programs like bc to divide a decimal.
beep -f "${1}" -l "${2}" < /dev/null& # Play sound by built-in speaker system
( speaker-test --test sine --frequency "${1}" ) > /dev/null& pid=$! ; sleep "${second}s" ; kill -9 "${pid}"
fi
}
This function plays sound through the speakers and the built-in speaker system. We use “speaker-test” to create sound through the speakers and “beep” command to create sound through the internal PC speaker. As you know, you can’t directly divide a decimal into a Bash script, To do this, we need to use commands like “bc”.
The rest of the functions need not be explained:
play_notice_sound () {
### This function plays notice sounds
if (("${1}"==1)); then # $1 is for last minute notice
for i in 1 2
do
sleep 0.5s
play_sound 800 80
sleep 0.2s
play_sound 800 80
done
fi
if (("${1}"==2)); then # $2 is for accept user request more time
play_sound 1000 80
sleep 0.2s
play_sound 1200 100
fi
}
is_user_working () {
### This function checks whether the user has been working with the system or not.
### This function checks for both keyboards and mice files,
### if their size is zero, it means the user is not working with the system.
miceFileSize=$(stat --format %s "${mice_file}") # Get mice file size
kbdFileSize=$(stat --format %s "${keyboards_file}") # Get keyboards file size
echo > "${mice_file}" > "${keyboards_file}" # Clear mice and keyboards files
if ((miceFileSize > 1 )) || ((kbdFileSize > 1 )); then
true # return true
else
false
fi
}
end_work () {
### This function Execute commands related to the end of working time
play_sound 800 500
play_sound 700 500
sleep 5s
xset -display "${DISPLAY}" dpms force off # Turn off the monitor
}
start_work () {
### This function Execute commands related to the start working time
play_sound 1000 400
play_sound 1200 500
play_sound 1200 700
xset -display "${DISPLAY}" dpms force on # Turn on the monitor
}
Okay, we checked all the functions. Now it’s time to see the main function of the script together:
main() {
check_run_as_root # Check script execution with super user
if detect_script_running; then # This script should not run multiple times at same time
echo "Script is runnig ..."
exit 1
fi
load_variables # Load variables from configuration file
dump_keyboard_and_mouse # Store keyboards and mice activity in the files
while true # Run the script forever
do
idleCounter=0โโ
whileCounter=0
while ((whileCounter < workingTime)); # This loop runs every minute until the end of workingTime.
do
load_variables
if "${active}"; then # This variable loads from configuration file
echo "You have" $((workingTime-whileCounter)) "minutes to work ... " > "$status_file"
if is_last_minute; then # Detect last minute of working time
echo > "${keyboards_file}" # Clear keyboards_file
play_notice_sound 1 # Play notice sound, The user can request more time to work
# after hearing this sound
for forCounter in {1..7} # Wait a short time for the user to request extra time
do
sleep 2s
if check_more_time_requested; then # Did the user request more time?
((whileCounter-=2)) # Add 2 minutes to user deadline
play_notice_sound 2 # Play audio to accept user request
break
fi
done
fi
sleep 1m # Wait for 1 minutes
if "${idleDetection}"; then # If IdleDetection is enable in the configuration file
if is_user_working; then
((whileCounter++)) # If the user is working, reduce one minute of working time.
idleCounter=0
else
((idleCounter++))
fi
if ((idleCounter >= restingTime)); then
# If the user does not work for 5 minutes then reset all the counters.
# This means that the user has not been with the system for a while,
# so, the user has enough rest and can work again.
whileCounter=0
idleCounter=0
fi
else # If IdleDetection is disable. static mode
idleCounter=0
((whileCounter++))
fi
else
idleCounter=0
whileCounter=0
sleep 1m
fi
done
echo "It's time to rest !" > "${status_file}"
end_work # Executing commands related to the end of working time
sleep "${restingTime}m"
start_work # Executing commands related to the start working time
done
}
I don’t think the main function needs explanation. Remember, to run the script, we have to add this code at the end of the script to Bash know that which function is the main function of the program:
main "${@}" # Start main function
Click here to download the script and required files.
This script is writen in “Sublime”
After downloading the script, use the following command to extract the compressed file :
tar -zxvf mazKnez_RestTime.tar.gz
Before you can run the script you need to convert the script to an executable file, using the following command:
chmod +x mazKnez_RestTime/restTime.sh
For run the script, go to “mazKnez_RestTime” directory and use this command :
bash restTime.sh &
If you want the script to run automatically every time the system is turned on, you can either insert it into a “/etc/rc.local” file or create a service for it. If you want to run this script automatically (via service or in rc.local), you need to include in the file “.profile” the following commands to access the monitor :
########################### [-- mazKnez RestTime ################################
xhost +SI:localuser:root > /dev/null # access root user to monitor
#xhost +
########################### mazKnez RestTime --] ################################
In this section, we create a very simple service to run scripts automatically, we use systemd and “systemctl” to build scripts on Debian Linux based operating systems. First of all, we need to create a file with the following contents in path “/etc/systemd/system/restTime.service” :
[Unit]
Description=mazKnez_restTime_script(mazKnez.com)
[Service]
ExecStart=/home/$YOUR-USERNAME/mazKnez_RestTime/restTime.sh
[Install]
WantedBy=default.target
I’ll talk later about building a detailed Linux service, here just enter the script path in the line specified. Then run the following command:
chmod 664 /etc/systemd/system/restTime.service
Run these commands now :
systemctl daemon-reload
systemctl enable restTime.service
systemctl start restTime.service
Congratulations, it’s over! I hope you find this helpful.
I’m waiting for your comments.
Have a nice day ๐
My nickname is mazKnez. I’m a computer programmer and a network administrator. my main reason for creating this website is to learn English. I want to write about my interests, like the IT world(programming, networking and …), nature, travels, mountaineering, movies and etc. I try to enjoy my life as much as I can and ti do all things that I like. Please tell me about my mistakes whenever you spot them. my English still needs lots of improvement! Thank you.
I think that what you typed made a ton of sense.
However, consider this, what if you wrote a catchier post title?
I am not suggesting your content isn’t good., but what if you added a post
title that grabbed a person’s attention? I mean Linux Bash script to remind you of the break time(RestTime) – mazKnez Personal Website is kinda vanilla.
You could peek at Yahoo’s home page and see how they create news headlines to
grab viewers to click. You might add a video or a picture or two to grab people excited about what you’ve written. Just my opinion,
it could make your website a little bit more interesting.
Hi dear friend ๐ ,Thank you very much for reading this post and for writing your comment.
I read your comment, but since my English is bad, I didn’t understand some of the sentences. What do you mean by “kinda vanilla”?
Hi dear friend ๐ ,Thank you very much for reading this post and for writing your comment.
Awesome post! Keep up the great work! ๐
Hi dear friend ๐ ,Thank you very much for reading this post and for writing your comment.
Hi dear friend ๐ ,Thank you very much for reading this post and for writing your comment.
Great content! Super high-quality! Keep it up! ๐
Thank you ๐
Hello everyone, it’s my first pay a quick visit at this web
page, and post is genuinely fruitful for me,
keep up posting these types of posts.
Hi dear friend ๐ ,Thank you very much for reading this post and for writing your comment.
Like!! Really appreciate you sharing this blog post.Really thank you! Keep writing.
Thank you ๐
Thanks ๐