Files
bikeslop/README.md
T
ryan a1e6b98ca9 Add screenshot to README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-27 10:07:43 -04:00

3.1 KiB
Raw Blame History

bikeslop

A local web app for visualising Garmin cycling GPX files with AI coaching insights powered by a local LLM.

bikeslop screenshot

Features

  • Route map — Leaflet.js map with per-segment polylines coloured by heart-rate intensity zone
  • Elevation & HR charts — Chart.js profiles plotted against cumulative distance
  • Stats sidebar — distance, duration, speed, elevation gain, avg/max HR, HR zone breakdown (Karvonen)
  • AI coaching insights — streaming post-ride feedback from a local LLM (via OpenAI-compatible API)
  • Garmin sync — fetch new rides directly from Garmin Connect, including MFA support
  • Imperial/metric toggle — persisted in localStorage

Requirements

Setup

# 1. Clone and install dependencies
git clone https://git.torrtle.co/ryan/bikeslop
cd bikeslop
uv sync

# 2. Drop your GPX files into garmin_gpx_exports/
#    (or use the in-app "Fetch from Garmin" button)

# 3. Start the server
LLAMACPP_BASE_URL=http://localhost:8080/v1 uv run uvicorn app:app --reload

Open http://localhost:8000 in your browser.

Configuration

Env var Default Description
LLAMACPP_BASE_URL http://localhost:8080/v1 Base URL of your OpenAI-compatible LLM endpoint

The model name is auto-discovered from GET /v1/models at startup. If the endpoint is unreachable, insights generation is still available — only that feature degrades.

GPX files

Place .gpx files exported from Garmin Connect (or any GPX-compliant device) in the garmin_gpx_exports/ directory. The app parses all files at startup. Supported data:

  • Track points with lat/lon/ele/time
  • Heart rate via the Garmin extension namespace (ns3:hr)

Activities with no track points (e.g. indoor rides) are listed with an "Indoor" badge and shown without a map or charts.

Standalone GPX downloader

main.py is a standalone script for bulk-downloading your Garmin Connect cycling activities as GPX files without running the web server:

uv run python main.py

API

Method Path Description
GET /api/rides Summary list of all rides (no track points)
GET /api/rides/{id} Full ride metadata + cached insight
GET /api/rides/{id}/points Full lat/lon/ele/hr array
POST /api/rides/{id}/insights?rhr=60 Stream AI coaching insight
POST /api/insights/generate-all?rhr=60 SSE stream — generate insights for all rides
POST /api/fetch-rides SSE stream — fetch new rides from Garmin Connect
POST /api/mfa Submit MFA code for an in-progress Garmin session

HR zone boundaries (Karvonen)

Zones are calculated from your resting HR (configurable in the UI) and the lifetime max HR observed across all rides:

Zone % of HRR
Z1 < 50%
Z2 5060%
Z3 6070%
Z4 7080%
Z5 > 80%

License

MIT