Deprecated: Function get_magic_quotes_gpc() is deprecated in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 99

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 619

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1169

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176
8000 GitHub - andrewgy8/zwift2fit: A tool to convert Zwift workout files (.zwo) to FIT workout files (.fit).
Nothing Special   »   [go: up one dir, main page]

Skip to content

andrewgy8/zwift2fit

Repository files navigation

Zwift2Fit

A Python tool to convert Zwift workout files (.zwo) to FIT workout files (.fit) for use with cycling computers and training platforms. Available as both a command-line tool and a web application.

Overview

This tool allows you to convert Zwift's proprietary workout format into the industry-standard FIT format, making your Zwift workouts compatible with Garmin, Wahoo, and other cycling computers that support structured workouts.

Features

  • Web Interface: Easy-to-use web application for drag-and-drop conversion
  • Command Line Tool: Batch processing for advanced users
  • Complete Workout Support: Handles all Zwift workout segments (Warmup, SteadyState, IntervalsT, Cooldown)
  • Power Zone Conversion: Converts relative power zones to absolute watts based on your FTP
  • Error Handling: Robust error handling with detailed feedback
  • Preserves Metadata: Maintains workout names and structure
  • Cross Platform: Works on Windows, macOS, and Linux

Supported Workout Elements

ZWO Element Description FIT Conversion
Warmup Progressive power ramp Warmup step with average target power
SteadyState Fixed power segment Active step with target power
IntervalsT Repeated work/rest intervals Separate work and rest steps

Quick Start

Option 1: Web Interface (Recommended)

The easiest way to convert your ZWO files:

# Install dependencies
uv sync

# Start the web application
uv run python start_webapp.py

Then open your browser to http://localhost:5000 and:

  1. Upload your .zwo file
  2. Enter your FTP value
  3. Click "Convert to FIT"
  4. Download the converted file

Option 2: Command Line

For batch processing or automation:

uv run python zwift2fit.py

Installation

Prerequisites

  • Python 3.11+
  • uv (recommended) or pip

Install with uv

git clone [repository-url]
cd zwift2fit
uv sync

Install with pip

git clone [repository-url]
cd zwift2fit
pip install -r requirements.txt

Usage

Web Application

Start the web server:

uv run python start_webapp.py

Features:

  • Drag & Drop Interface: Simply drop your .zwo file onto the upload area
  • Real-time Validation: Instant feedback on file format and FTP values
  • Secure Processing: Files are processed securely and cleaned up automatically
  • Download Management: Converted files are automatically named and downloaded
  • Error Handling: Clear error messages for troubleshooting

Command Line Tool

Single File Conversion

from zwift2fit import convert_zwo_to_fit

# Convert with default 250W FTP
convert_zwo_to_fit("my_workout.zwo", "my_workout.fit")

# Convert with custom FTP
convert_zwo_to_fit("my_workout.zwo", "my_workout.fit", ftp=275)

Batch Conversion

# Convert all .zwo files in current directory
uv run python zwift2fit.py

# The script will prompt for:
# - Input directory (default: current directory)
# - Output directory (default: same as input)
# - FTP value (default: 250W)

Docker Deployment

For production deployment:

# Build and run with Docker Compose
docker-compose up -d

# Or build manually
docker build -t zwift2fit-web .
docker run -p 5000:5000 zwift2fit-web

API Usage

The web application also provides a REST API:

# Convert via API
curl -X POST http://localhost:5000/api/convert \
  -F "zwo_file=@workout.zwo" \
  -F "ftp=250" \
  --output workout.fit

Configuration

FTP Setting

The script converts relative power zones (e.g., 0.88 = 88% FTP) to absolute watts. Set your FTP appropriately:

# For a cyclist with 250W FTP
batch_convert_zwo_to_fit("./workouts", ftp=250)

# For a cyclist with 300W FTP  
batch_convert_zwo_to_fit("./workouts", ftp=300)

File Paths

# Same directory (overwrites with .fit extension)
convert_zwo_to_fit("workout.zwo")

# Specific output path
convert_zwo_to_fit("workout.zwo", "converted/workout.fit")

# Batch with different directories
batch_convert_zwo_to_fit("./zwo_files", "./fit_files")

Example Workout Structure

Input (.zwo file):

<workout_file>
  <name>Pacing1</name>
  <workout>
    <Warmup Duration="420" PowerLow="0.5" PowerHigh="0.75" />
    <SteadyState Duration="180" Power="0.88" />
    <IntervalsT Repeat="5" OnDuration="60" OffDuration="60" OnPower="0.9" OffPower="0.7"/>
  </workout>
</workout_file>

Output (.fit file):

  • Warmup: 7 minutes ramping from 125W to 188W (avg 156W)
  • Steady: 3 minutes at 220W (88% of 250W FTP)
  • Intervals: 5x (1 min at 225W / 1 min at 175W)

File Structure

your_project/
├── zwo_to_fit_converter.py    # Main script
├── zwift_workouts/            # Input directory
│   ├── workout1.zwo
│   ├── workout2.zwo
│   └── ...
└── fit_files/                 # Output directory
    ├── workout1.fit
    ├── workout2.fit
    └── ...

Error Handling

The script includes robust error handling:

  • Invalid XML: Skips corrupted .zwo files and continues
  • Missing Elements: Uses sensible defaults for missing workout data
  • File I/O Errors: Reports errors but continues batch processing
  • Progress Reporting: Shows conversion status for each file

Troubleshooting

Common Issues

"No .zwo files found"

  • Check the input directory path
  • Ensure files have .zwo extension
  • Verify file permissions

"Error converting [file]"

  • Check if the .zwo file is valid XML
  • Ensure the workout structure follows Zwift format
  • Try converting the file individually for detailed error info

FIT file not recognized by device

  • Verify your FTP setting is reasonable (50-500W typically)
  • Check that workout duration isn't too long for your device
  • Some older devices may have limited FIT workout support

Validation

To verify conversion success:

  1. Check that .fit files are created with reasonable file sizes
  2. Import a test file into your cycling computer or training software
  3. Verify power targets match expected values based on your FTP

Technical Details

FIT File Structure

The converter creates FIT files with these message types:

  • FILE_ID: Identifies the file as a workout
  • WORKOUT: Contains workout metadata (name, sport)
  • WORKOUT_STEP: Individual workout segments with duration and power targets

Power Zone Mapping

Zwift Zone Typical % FTP Description
0.5 50% Recovery
0.7 70% Endurance
0.88 88% Tempo/Sweet Spot
0.9 90% Threshold
1.0+ 100%+ VO2 Max/Neuromuscular

Contributing

Feel free to submit issues, feature requests, or pull requests. Common enhancement ideas:

  • Support for additional ZWO elements (Cooldown, Ramp, etc.)
  • Heart rate zone targets
  • Cadence targets
  • GUI interface
  • More sophisticated FIT file validation

License

This project is provided as-is for personal use. The FIT file format is owned by Garmin/ANT+.

Compatibility

  • Python: 3.6+
  • Zwift: All .zwo workout file versions
  • FIT Devices: Garmin Edge, Wahoo ELEMNT, and other FIT-compatible cycling computers
  • Platforms: Windows, macOS, Linux

About

A tool to convert Zwift workout files (.zwo) to FIT workout files (.fit).

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published
0