Monitoring Usage time in i3

i3 the X window manager, organizes the screen into multiple workspaces. I allocate each workspace to specific apps with similar functionality. For eg., my workspace 2 (named as 2: web) is used for browsing online web. I either use chromium or firefox (iceweasel) to browse, so both apps are configured to show up in workspace "2: web" via the assign command in ~/.i3/config

    assign [class="(?i)chromium"] 2: web
assign [class="(?i)iceweasel"] 2: web
 
for_window [class="(?i)chromium"] workspace number 2
for_window [class="(?i)iceweasel"] workspace number 2


For convenience, once the browser launches, it will switch to workspace 2. This is configured via the for_window command.


i3-ws-monitor script make use of i3status, which generates a status line for i3bar. The interval directive (in i3status config) specifies the time in seconds for which i3status will sleep before printing the next status line. Running i3-ws-monitor as a replacement for i3status, the script checks which is the current visible workspace and then increment the current workspace usage time by interval seconds. (It makes the assumption that for the past interval seconds, the screen is in the current workspace)

The script outputs i3status status line together with the current workspace usage time (in > h:mm:ss format) inserted in front. The usage time is displayed in color (variable warnStatusCol) if the workspace usage time is within some time (variable warnStatusInSecs) of the workspace configured maximum time in mins (variable warnifWSMins) or the total time spent is within warnStatusInSecs of the configured maximum time in mins (variable warnifMins). In the case when the total time spent is closer to its maximum, the status line will have a prefix $ (eg $> 2:59:00 ).

i3-ws-monitor will give a warning message when either the total time spent has reached or exceeded the configured maximum time in mins (variable warnifMins), or the time spent in current workspace has reached or exceeded the configured maximum time for the workspace in mins (variable warnifWSMins). Beside showing the warning message, it will switch to a configured unused workspace. (variable ignoreWS) This workspace should not be used by the user, for the script does not compute usage time in this workspace. And, it will display the total time spent in the status.

Usage time is saved periodically (variable saveMins) to a configured existing directory (variable savetoDir) in json format.

i3-ws-monitor.config file is a file that defines the various configured variables, is simply sourced by the script i3-ws-monitor.
The "declare -A warnifWSMins" declares a bash associative array.
warnifWSMins define the maximum time for the workspaces, where each key is the workspace name, and the value is maximum time for the workspace in mins.

i3-ws-monitor.config

    #  i3status output interval = 5 s (as in ~/.config/i3status/config)
interval=5
# gives warning if total time spent >= ? mins
warnifMins=180
# saves time spent every ? mins
saveMins=30
# save to dir
savetoDir="$HOME/personal/times"
# ignores workspace 10 (unused workspace, also for showing warning)
ignoreWS='10'
# gives warning if time spent in workspace >= ? mins
declare -A warnifWSMins
warnifWSMins=([2: web]=60 [9: ebook]=45)
# warning status color
warnStatusCol="#FF0000"
# warning status if within ? secs of max time (either workspace or total)
warnStatusInSecs=60


To replace i3status with i3-ws-monitor (assume script & config are within PATH), the below is modified in ~/.i3/config

    bar {
# status_command i3status
status_command i3-ws-monitor
}



i3-ws-monitor script also make use of:
jq, a command-line JSON processor, to read back the saved usage data; and to filter and read the workspaces data from i3. notify-send - a command-line program to send desktop notifications.

i3-ws-monitor

    #!/bin/bash
# monitor time in i3 workspace, via replacing i3status
# save every 1/2 hour
# i3status output interval = 5 (in ~/.config/i3status/config)
# gives warning if total time spent >= warnifMins mins
# ignores workspace 10 (ignoreWS) for time spent
# show workspace time at front of status line
. i3-ws-monitor.config
#interval=5
warnif=$((warnifMins*60))
hour=$((saveMins*60))
runfor=0
runtotal=0
date=$(date "+%Y%m%d")
saveto="$savetoDir/$date"
ignorews="\"$ignoreWS\""
declare -A wstimes
declare -A warnifWSSecs
if ((${#warnifWSMins[@]} > 0)); then
for ws in "${!warnifWSMins[@]}"; do
warnifWSSecs["$ws"]=$((warnifWSMins["$ws"]*60))
done
fi
 
readData() {
local total=0
while read -r key ; do
read -r val
wstimes["$key"]=$val
total=$((total+val))
done < <(jq -r 'to_entries|.[]|select(.key!="")|.key, .value' "$saveto")
runtotal=$total
}
 
save() {
{
echo "{" >&3
for ws in "${!wstimes[@]}"; do
echo "\"$ws\": ${wstimes[$ws]}," >&3
done
# json do not support trailing ,
echo '"":0' >&3
echo "}" >&3
} 3>$saveto
}
 
# show workspace time at front of status line
# in workspace ignoreWS, show total time
wsStatus() {
local ws=$1
local line=$2
local aidx=$(expr index "$line" "[")
if (( $aidx == 0 )); then
echo "$line" || exit 1
return
fi
local time
local wsline
if [ "$ws" == "" ]; then
time=$runtotal
else
time=wstimes["$ws"]
fi
local sec=$((time % 60))
local min=$((time / 60))
if (( $min >= 60 )); then
local hr=$((min / 60))
min=$((min % 60))
printf -v wsline "> %d:%02d:%02d" $hr $min $sec
else
printf -v wsline "> %02d:%02d" $min $sec
fi
# "color":"#FF0000"
local warn=""
local tomax=$((warnif - runtotal));
local towsmax
if [ "$ws" != "" ] && [ ${warnifWSMins["$ws"]} ]; then
towsmax=$((warnifWSSecs["$ws"] - time))
else # workspace max is not set
towsmax=$((warnStatusInSecs + time))
fi
if (( $tomax <= $warnStatusInSecs )) || (( $towsmax <= $warnStatusInSecs )); then
warn="\"color\":\"$warnStatusCol\","
# status is prefix with $ if total time is closer to its max
if (( $tomax < $towsmax )); then
wsline="\$$wsline"
fi
fi
# [ {"name":"wstime","full_text":"> ?:??:??"},
wsline="{\"name\":\"wstime\",$warn\"full_text\":\"$wsline\"},"
echo "${line:0:$aidx}${wsline}${line:$aidx}" || exit 1
}
 
# read saved data if it exist
if [ -f "$saveto" ]; then
readData
fi
 
i3status | while read line;
do
if [ "${line: -1}" != "]" ]; then
echo "$line" || exit 1
continue
fi
ignores=0
ws=$(i3-msg -t get_workspaces | jq '.[] | select(.visible==true).name')
if [ "$ws" != "$ignorews" ]; then
ws=${ws%\"}
ws=${ws#\"}
((wstimes["$ws"]+=interval))
runtotal=$((runtotal+interval))
 
if (($runtotal >= $warnif)); then
i3-msg -q "workspace $ignoreWS"
notify-send -u critical "Exceeded $warnifMins mins!\nPlease stop!!"
ignores=1
elif [ ${warnifWSMins["$ws"]} ]; then
if ((${wstimes["$ws"]} >= ${warnifWSSecs["$ws"]})); then
i3-msg -q "workspace $ignoreWS"
notify-send -u critical "Exceeded workspace [$ws] ${warnifWSMins["$ws"]} mins!\nPlease stop!!"
ignores=1
fi
fi
else
ignores=1
fi
if (( $ignores == 0 )); then
wsStatus "$ws" "$line"
runfor=$((runfor+interval))
tosave=$((runfor >= hour))
else # show total
wsStatus "" "$line"
#echo "$line" || exit 1
tosave=$((runfor > 0))
fi
if (($tosave == 1)); then
save
runfor=0
fi
done



The basic design here is similar to my android app App Gatekeeper.

Each workspace serves as a functional grouping, and a usage limit can be assigned to restrict the time spent. Here, there is a total usage limit, while in App Gatekeeper, there are separate weekend limits, and separate limits for individual apps and categories of apps, and specific times when you can access the apps.

It's a marvel of the simplicity of the code here in contrast to the complexity of my android code!


Related:
Curb your phone & tablet Addiction with App Gatekeeper

Comments

blog comments powered by Disqus