Chyuang

Back
Screen Time Tracker

Screen Time Tracker

Grafana dashboard for visualizing ActivityWatch data. Tracks app usage, AFK status, and browser activity.

Architecture

ActivityWatch app records events
↓
SQLite backup via sqlite3 .backup
↓
↓
Dashboard

Panels

AFK Status (state-timeline)

SELECT strftime('%s', timestamp) as time,
  CASE WHEN json_extract(datastr, '$.status') = 'not-afk'
    THEN 1 ELSE 0 END as active
FROM eventmodel WHERE bucket_id = 6

Green = active, red = AFK. Uses value mapping for colors.

Top Apps / Top Sites (bar charts)

SELECT json_extract(datastr, '$.app') as app,
  SUM(duration)/60.0 as minutes
FROM eventmodel WHERE bucket_id = 5
GROUP BY app ORDER BY minutes DESC LIMIT 5

Top 5 apps/sites by duration. Sites extracted from URL field, strips protocol and www.

App Timeline (state-timeline)

"enumConfig": {
  "text": ["Chrome", "Code", "Terminal", ...]
  "color": ["#1934e5", "#e0c351", ...]          // auto-generated
}

Each app gets a unique color. But how?

Auto-coloring apps

Grafana's state-timeline doesn't auto-assign colors to string values (#38806, open since 2021). With hundreds of apps, manual configuration isn't practical.

Solution: Python script generates colors at container startup before Grafana loads.

docker compose up
↓
enum-generator runs once (queries DB → generates colors → writes JSON)
↓
Grafana starts (depends_on: service_completed_successfully)
def hash_to_color(name):
    h = int(hashlib.md5(name.encode()).hexdigest(), 16)
    hue = (h * 137.508) % 360  # golden angle for max spread
    # ... HSL to RGB conversion
    return f"#{r:02x}{g:02x}{b:02x}"

Golden angle (137.5°) ensures consecutive apps get maximally different hues.