No description
  • Rust 58.1%
  • HTML 31.6%
  • CSS 6.5%
  • JavaScript 3.5%
  • Just 0.3%
Find a file
Test 45b49fbbf6 Render activity stats as a table and add a heart-rate chart
The stat cards are now a table, easier to scan than the grid. The
detail page also plots per-minute heart rate for the activity's time
range with uPlot, reusing the existing /api/activity endpoint.

An elevation graph was considered but dropped: this export has no
per-timestamp altitude series anywhere in the DB (every altitude-
bearing table is empty), only the aggregate gain/loss/min/max already
in SUMMARY_DATA and shown in the stats table.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 20:30:16 +02:00
src Add an activities list and detail page 2026-09-13 20:29:58 +02:00
static Render activity stats as a table and add a heart-rate chart 2026-09-13 20:30:16 +02:00
templates Render activity stats as a table and add a heart-rate chart 2026-09-13 20:30:16 +02:00
.gitignore Initial project setup: gitignore and README 2026-09-13 10:05:01 +02:00
Cargo.lock Render pages with Askama templates instead of static HTML 2026-09-13 19:40:28 +02:00
Cargo.toml Render pages with Askama templates instead of static HTML 2026-09-13 19:40:28 +02:00
DB-FORMAT.md Document general Gadgetbridge SQLite schema 2026-09-13 10:05:09 +02:00
DB-INVESTIGATION.md Document findings from the actual Gadgetbridge.db export 2026-09-13 10:05:15 +02:00
Justfile Add Justfile with a default serve command 2026-09-13 10:25:49 +02:00
README.md Add an activities list and detail page 2026-09-13 20:29:58 +02:00

gadgetbridge-graph

A project to create graphs from a Gadgetbridge database — the SQLite export produced by the Gadgetbridge Android app, which stores activity, sleep, heart rate, and other data synced from Bluetooth wearables.

See DB-FORMAT.md for general Gadgetbridge database schema documentation and DB-INVESTIGATION.md for findings specific to the sample database used during development.

Running

The server reads Gadgetbridge.db in the working directory (override with GADGETBRIDGE_DB_PATH) and serves the API plus the static frontend on port 3000 (override with PORT):

just

This builds, runs, and opens http://localhost:3000/ in your browser. cargo run works too if you don't want the browser opened for you.

API

  • GET /api/activity?from=<unix_seconds>&to=<unix_seconds> — per-minute steps/heart rate/intensity samples from GARMIN_ACTIVITY_SAMPLE.
  • GET /api/body-battery?from=<unix_seconds>&to=<unix_seconds> — Body Battery (0–100) from GARMIN_BODY_ENERGY_SAMPLE.
  • GET /api/stress?from=<unix_seconds>&to=<unix_seconds> — stress score (0–100) from GARMIN_STRESS_SAMPLE.
  • GET /api/spo2?from=<unix_seconds>&to=<unix_seconds> — blood oxygen % from GARMIN_SPO2_SAMPLE.
  • GET /api/respiratory-rate?from=<unix_seconds>&to=<unix_seconds> — breathing rate from GARMIN_RESPIRATORY_RATE_SAMPLE.
  • GET /api/sleep-stages?from=<unix_seconds>&to=<unix_seconds> — sleep-stage transitions from GARMIN_SLEEP_STAGE_SAMPLE (event-based, not fixed-interval).
  • GET /api/sleep-nights?from=<unix_seconds>&to=<unix_seconds> — one row per night (sleep score, restless-moment count, HRV last-night average) joined from GARMIN_SLEEP_STATS_SAMPLE, GARMIN_SLEEP_RESTLESS_MOMENTS_SAMPLE, and GARMIN_HRV_SUMMARY_SAMPLE.
  • GET /api/charging-sessions — every charge-up derived from BATTERY_LEVEL (a maximal run of non-decreasing samples), as {start_timestamp, start_level, end_timestamp, end_level}.
  • GET /api/daily-steps — total steps per local calendar day, as {date, steps} (date is YYYY-MM-DD), for every day with data.
  • GET /api/activities — every logged workout from BASE_ACTIVITY_SUMMARY, most recent first.
  • GET /api/activities/<id> — full detail (curated stats parsed out of SUMMARY_DATA) for one activity, or 404.

More query endpoints will be added as new graphs/facts are needed.

Pages

  • / — heart rate, Body Battery, stress, SpO2, and respiratory rate over a selectable time range.
  • /activities — logged workouts (walks, rides, hikes, ...) from most recent to oldest.
  • /activities/<id> — detail page for one activity: distance, duration, heart rate, training effect, and other stats found in its SUMMARY_DATA.
  • /calendar.html — a month calendar with a per-day step gauge, sized against the highest daily step count ever recorded and colored on a red-yellow-green gradient by progress toward a configurable daily target (green at or above it, red at or below half of it).
  • /sleep.html — a per-night sleep-stage hypnogram, plus nightly trends for sleep score, restless moments, and HRV.
  • /fun-facts.html — charging frequency and the weekday you usually charge on.

Pages are rendered server-side with Askama templates in templates/. templates/layout.html holds the shared nav and page shell; each page template extends it and only fills in its own content block, so the header/footer stay consistent automatically. Shared styling lives in static/style.css, themed with the Solarized palette (dark by default, light via prefers-color-scheme). Charts are rendered with uPlot (vendored under static/vendor/uplot/, no CDN dependency) instead of hand-rolled canvas drawing; the shared setup for a time-series line chart lives in static/charts.js (createLineChart), used by both / and /sleep.html.

Development

just test
just lint