I’m currently in the process of switching dev machines, and as a part of that I had to revisit how I track my work time.
Last time I rolled my own script that just tracked the time I was actively using my work machine. But this time around I decided to instead track the time that ActivityWatch says I’m actually working.
Turns out querying this data isn’t at all intuitive, so thought I’d share the script I ended up with here:
The Bash Script
#!/bin/bash
# Accept optional date argument (format: YYYY-MM-DD), defaults to today
target_date="${1:-$(date +%Y-%m-%d)}"
# Get timestamps for the target date
day_start=$(date -d "$target_date" +%Y-%m-%dT00:00:00%:z)
day_end=$(date -d "$target_date + 1 day" +%Y-%m-%dT00:00:00%:z)
timeperiod="${day_start}/${day_end}"
# Fetch settings including categories
settings=$(curl -s 'http://localhost:5600/api/0/settings')
# Extract categories from settings and transform to query format
# Settings format: {"name": [...], "rule": {...}} -> Query format: [[...], {...}]
# Filter out categories with null type (like "Uncategorized")
categories=$(echo "$settings" | jq -c '[.classes[] | select(.rule.type != null) | [.name, .rule]]')
# Build the query with dynamic categories
query=$(jq -n --argjson cats "$categories" --arg tp "$timeperiod" '{
"query": [
"afk_events = query_bucket(find_bucket(\"aw-watcher-afk_\"));",
"window_events = query_bucket(find_bucket(\"aw-watcher-window_\"));",
"window_events = filter_period_intersect(window_events, filter_keyvals(afk_events, \"status\", [\"not-afk\"]));",
"merged_events = merge_events_by_keys(window_events, [\"app\", \"title\"]);",
("merged_events = categorize(merged_events, " + ($cats | tostring) + ");"),
"RETURN = merged_events;",
";"
],
"timeperiods": [$tp]
}')
# Run query and filter for Work category, then sum durations
curl -s 'http://localhost:5600/api/0/query/' \
-X POST \
-H 'Content-Type: application/json' \
--data-raw "$query" | jq '[.[0][] | select(.data["$category"][0] == "Work") | .duration] | add'
In my case, I turn around and use another script to then sync that data to Beeminder using Buzz.
You can find this script plus other Beeminder-related scripts and Linux mods in my dotfiles repository.