Skip to main content

pace2fit

CI PyPI License

Generate Garmin FIT workout files from a simple text description.

A public instance is running at pace2fit.eu -- no install required, just open the page, type a workout, and download the .fit file.

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
GIT_COMMIT=$(git rev-parse --short HEAD) 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
GIT_COMMIT=$(git rev-parse --short HEAD) docker compose up -d --build

Dependencies

Metadata

Release files for pace2fit 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pace2fit 0.1.3
File Size Uploaded Provenance Actions
pace2fit-0.1.3.tar.gz 28.6 kB Without provenance Details for pace2fit-0.1.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pace2fit 0.1.3
File Interpreter ABI Platform Actions
pace2fit-0.1.3-py3-none-any.whl Python 3 none any Details for pace2fit-0.1.3-py3-none-any.whl

Total release size: 61.0 kB

Release files / pace2fit-0.1.3.tar.gz

Download URL pace2fit-0.1.3.tar.gz
Size 28.6 kB
Tags Source
SHA-256 checksum
How to use checksums
87ca03ac608d92cd55d0e8593eb4e35f11d897dc071af769d48cdf162b261d98
BLAKE2b-256 checksum
How to use checksums
fb2f994c2952242bfa60e59b8b73f7782e6f438411ec7808f8bda39c8078a1d9
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / pace2fit-0.1.3-py3-none-any.whl

Download URL pace2fit-0.1.3-py3-none-any.whl
Size 32.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
876255ffcc8eee39aaa5fffffa051a94a0d4e9c88d8e43a9756dd24678f90061
BLAKE2b-256 checksum
How to use checksums
dd359557262dac62f4fdbf6c7da08d6def989a50378089aa06b5575797274146
Upload date
Uploaded using Trusted Publishing?
What is 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}

Security for pace2fit 0.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page