Skip to main content

Generate Garmin FIT workout files from a simple DSL

Project description

pace2fit

CI PyPI License

Generate Garmin FIT workout files from a simple text description.

pace2fit generate 'warmup@6:00,3x(8min@threshold+2min@easy),cooldown@6:30' -o workout.fit

Quick start

No install needed -- run directly with uvx:

uvx pace2fit generate '10min@Z2'

Or from the git repo directly:

uvx --from git+https://codeberg.org/Solal/pace2fit pace2fit generate '10min@Z2'

Install

Requires Python 3.10+.

uv sync

Or install as a tool:

uv tool install .

Usage

Generate a workout

# Simple zone workout
pace2fit generate '10min@Z2'

# Structured workout with intervals
pace2fit generate 'warmup@6:00,3x(8min@threshold+2min@easy),cooldown@6:30' \
  -o thursday.fit -n 'Thursday Threshold'

# Distance-based
pace2fit generate '5km@5:30-6:00' -o easy_run.fit

Options

pace2fit generate [OPTIONS] WORKOUT

Arguments:
  WORKOUT    Workout description string (required)

Options:
  -o, --output PATH    Output .fit file path (default: workout.fit)
  -n, --name TEXT      Workout name (default: the input string)
  --unit [km|mi]       Pace unit (default: km)
  --help               Show help and exit

Web interface

Start a local web UI for writing workouts in the browser:

pace2fit serve
pace2fit serve --port 3000

Open http://127.0.0.1:8000 in your browser. The page includes a DSL syntax cheatsheet with clickable examples. Dark mode follows your OS preference.

DSL Syntax

A workout is a comma-separated list of steps:

warmup@6:00, 10min@Z2, 3x(8min@4:30+2min@6:00), cooldown@6:30

Durations

Syntax Meaning
10min 10 minutes
30sec or 30s 30 seconds
5km 5 kilometres
400m 400 metres
warmup Open duration (lap button), warmup intensity
cooldown Open duration (lap button), cooldown intensity
open Open duration (lap button)

Targets

Targets are specified with @ after the duration.

Pace targets (min:sec per km):

Syntax Meaning
@5:30 5:30/km (auto-creates a +/-5s range)
@5:00-6:00 Pace range from 5:00 to 6:00/km

Heart rate zones:

Syntax Meaning
@Z1 .. @Z5 HR zone 1 through 5

Named pace zones:

Name Pace range
easy 6:00 - 6:30/km
recovery 6:30 - 7:00/km
tempo 5:00 - 5:15/km
threshold 4:30 - 4:45/km
marathon 5:00 - 5:20/km
hm 4:40 - 4:55/km
interval 3:45 - 4:15/km
repetition 3:30 - 3:45/km

Repeats

3x8min@threshold           # 3 repeats of a single step
3x(8min@5:00+2min@6:00)    # 3 repeats of work + recovery
4x(1min@Z4+1min@Z2+30s@Z5) # 3 steps repeated 4 times

Full examples

# Easy 30 min run
pace2fit generate '30min@easy'

# 10k tempo
pace2fit generate 'warmup@6:00,10km@tempo,cooldown@6:30' -n '10k Tempo'

# Classic threshold session
pace2fit generate 'warmup@6:00,3x(8min@threshold+2min@easy),cooldown@6:30'

# Track intervals
pace2fit generate 'warmup@6:00,8x(400m@Z5+400m@Z1),cooldown@6:30' -n 'Track 400s'

# Pyramid
pace2fit generate 'warmup@6:00,2min@Z3,4min@Z4,6min@Z4,4min@Z4,2min@Z3,cooldown@6:30'

Output

The generated .fit file can be loaded onto Garmin devices or uploaded to Garmin Connect as a workout.

Development

# Install dependencies
uv sync

# Install pre-commit hooks
prek install

# Run tests
uv run pytest

# Run tests with verbose output
uv run pytest -v

# Run all hooks manually
prek run --all-files

This project uses prek for pre-commit hooks. Hooks run automatically on every commit and include ruff (lint + format), pytest, and basic file hygiene checks. See prek.toml for the full configuration.

Deploying

pace2fit can be self-hosted using Docker behind a reverse proxy.

Prerequisites

  • A server with Docker installed
  • A reverse proxy (e.g. Caddy) with a domain pointing to your server

DNS

Add an A record for your domain pointing to your server's public IP:

@       IN      A       <your-server-IPv4>

Build and run

git clone ssh://git@codeberg.org/Solal/pace2fit.git
cd pace2fit
docker compose up -d --build

This builds the image and starts the container on 127.0.0.1:8000.

Caddy configuration

Add a block to your Caddyfile:

pace2fit.eu {
    reverse_proxy localhost:8000
}

Then reload Caddy:

sudo systemctl reload caddy

Caddy automatically provisions HTTPS via Let's Encrypt.

Updating

cd pace2fit
git pull
docker compose up -d --build

Dependencies

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pace2fit-0.1.2.tar.gz (27.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pace2fit-0.1.2-py3-none-any.whl (30.8 kB view details)

Uploaded Python 3

File details

Details for the file pace2fit-0.1.2.tar.gz.

File metadata

  • Download URL: pace2fit-0.1.2.tar.gz
  • Upload date:
  • Size: 27.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pace2fit-0.1.2.tar.gz
Algorithm Hash digest
SHA256 5002c869a8f12a5b40f6f4804f14c9ced3e12250c95953b0b6fb2d368d1d9320
MD5 527034ccdc044a9b52bd69910eaa8a7b
BLAKE2b-256 b3e4094d4edd1098e6ba7bc1fdc4875d2fbf91d2305976efd298e34efa10aab4

See more details on using hashes here.

File details

Details for the file pace2fit-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: pace2fit-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 30.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pace2fit-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e69da2ce9b9b6f03e73372b0948223939c241d55288023cdcb2a80ed5ae8e40e
MD5 fb52d92a69aaf0b60e8bea92349cd79f
BLAKE2b-256 fad8c2eda817f96dd41b1d4a68ec774e9edfb401468246ea6a138e53a8e9704c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page