Commands
Global Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| verbosity | v | Log level (trace, debug, info, warn, error, fatal, panic) | |
| monochrome | m | Use monochrome logging, color enabled by default | |
| json | j | Emit all results as JSON and print to stdout | |
| http-tracing | Log all http calls (warning: no effort is made to mask log ids, keys, and other sensitive information) | ||
| timeout | t | Timeout duration (eg, 1ms, 2s, 5m, 3h) | |
| help | h | show help |
Commands
- cyclinganalytics
- cyclinganalytics activities
- cyclinganalytics activity
- cyclinganalytics athlete
- cyclinganalytics oauth
- cyclinganalytics streamsets
- envvars
- help
- qp
- qp copy
- qp export
- qp list
- qp providers
- qp status
- qp upload
- rwgps
- rwgps activities
- rwgps activity
- rwgps athlete
- rwgps route
- rwgps routes
- strava
- strava activities
- strava activity
- strava athlete
- strava oauth
- strava photos
- strava refresh
- strava route
- strava routes
- strava streams
- strava streamsets
- strava update
- strava webhook
- strava webhook list
- strava webhook subscribe
- strava webhook unsubscribe
- version
- zwift
- zwift activities
- zwift activity
- zwift athlete
- zwift files
- zwift refresh
cyclinganalytics
Description
Operations supported by the CyclingAnalytics API
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| cyclinganalytics-client-id | CYCLINGANALYTICS_CLIENT_ID | CyclingAnalytics client id | |
| cyclinganalytics-client-secret | CYCLINGANALYTICS_CLIENT_SECRET | CyclingAnalytics client secret | |
| cyclinganalytics-access-token | CYCLINGANALYTICS_ACCESS_TOKEN | CyclingAnalytics access token | |
| cyclinganalytics-refresh-token | CYCLINGANALYTICS_REFRESH_TOKEN | CyclingAnalytics refresh token | |
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries |
cyclinganalytics activities
Description
Query the CyclingAnalytics API for a list of rides for the authenticated athlete
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| count | N | The number of activities to query from CA (the number returned will be <= N) |
cyclinganalytics activity
Description
Query the CyclingAnalytics API for the details of a specific ride for the authenticated athlete
Syntax
cyclinganalytics athlete
Description
Query the CyclingAnalytics API for the authenticated athlete's profile and display their account information
Syntax
Example
$ gravl ca t
{
"email": "me@example.com",
"id": 88827722,
"name": "That Guy",
"sex": "male",
"timezone": "America/Los_Angeles",
"units": "us"
}
cyclinganalytics oauth
Description
Start a local OAuth server to acquire access and refresh tokens for the specified provider
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| origin | Callback origin | ||
| port | Port on which to listen |
cyclinganalytics streamsets
Description
Query the CyclingAnalytics API for the set of available data streams that can be requested for a ride
Syntax
envvars
Description
Useful for creating a .env file for all possible environment variables
Syntax
help
Description
Shows a list of commands or help for one command
Syntax
qp
Description
Copy and synchronize activities between different activity platforms
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| cyclinganalytics-client-id | CYCLINGANALYTICS_CLIENT_ID | CyclingAnalytics client id | |
| cyclinganalytics-client-secret | CYCLINGANALYTICS_CLIENT_SECRET | CyclingAnalytics client secret | |
| cyclinganalytics-access-token | CYCLINGANALYTICS_ACCESS_TOKEN | CyclingAnalytics access token | |
| cyclinganalytics-refresh-token | CYCLINGANALYTICS_REFRESH_TOKEN | CyclingAnalytics refresh token | |
| rwgps-client-id | RWGPS_CLIENT_ID | RideWithGPS client id | |
| rwgps-access-token | RWGPS_ACCESS_TOKEN | RideWithGPS access token | |
| strava-client-id | STRAVA_CLIENT_ID | Strava client id | |
| strava-client-secret | STRAVA_CLIENT_SECRET | Strava client secret | |
| strava-refresh-token | STRAVA_REFRESH_TOKEN | Strava refresh token | |
| zwift-username | ZWIFT_USERNAME | Zwift username | |
| zwift-password | ZWIFT_PASSWORD | Zwift password |
qp copy
Description
Copy an activity from a source to a destination
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries | ||
| from | Source data provider | ||
| to | Sink data provider | ||
| overwrite | o | Overwrite the file if it exists; fail otherwise | |
| output | O | The filename to use for writing the contents of the export, | |
| if not specified the contents are streamed to stdout | |||
| poll | Continually check the status of the request until it is completed | ||
| interval | The amount of time to wait between polling for an updated status | ||
| iterations | N | The max number of polling iterations to perform |
Example
$ gravl qp copy --from zwift --to cyclinganalytics 934398333398662432
2021-10-28T06:25:55-07:00 INF created zwift client
2021-10-28T06:25:55-07:00 INF created cyclinganalytics client
2021-10-28T06:25:56-07:00 INF export exp=2021-10-26-18-19-39.fit id=934398333398662432
2021-10-28T06:25:57-07:00 INF poll id=1067278367 iteration=0
2021-10-28T06:26:00-07:00 INF poll id=1067278367 iteration=1
2021-10-28T06:26:00-07:00 INF counters count=1 metric=gravl.upload.file.success
2021-10-28T06:26:00-07:00 INF counters count=2 metric=gravl.upload.poll
2021-10-28T06:26:00-07:00 INF samples count=1 max=4.181048393249512 mean=4.181048393249512 metric=gravl.runtime min=4.181048393249512 stddev=0
$ gravl qp copy --from zwift --to cyclinganalytics 934398333398662432
2021-10-28T06:34:06-07:00 INF created zwift client
2021-10-28T06:34:06-07:00 INF created cyclinganalytics client
2021-10-28T06:34:07-07:00 INF export exp=2021-10-26-18-19-39.fit id=934398333398662432
2021-10-28T06:34:08-07:00 INF poll id=9819686356 iteration=0
{
"upload_id": 9819686356,
"status": "processing",
"ride_id": 0,
"user_id": 1603544,
"format": "fit",
"datetime": "2021-10-28T13:34:09",
"filename": "2021-10-26-18-19-39.fit",
"size": 113324,
"error": "",
"error_code": ""
}
2021-10-28T06:34:10-07:00 INF poll id=9819686356 iteration=1
{
"upload_id": 9819686356,
"status": "error",
"ride_id": 0,
"user_id": 1603544,
"format": "fit",
"datetime": "2021-10-28T13:34:09",
"filename": "2021-10-26-18-19-39.fit",
"size": 113324,
"error": "The ride already exists: 894091983723",
"error_code": "duplicate_ride"
}
2021-10-28T06:34:10-07:00 INF counters count=2 metric=gravl.upload.poll
2021-10-28T06:34:10-07:00 INF counters count=1 metric=gravl.upload.file.success
2021-10-28T06:34:10-07:00 INF samples count=1 max=3.807884454727173 mean=3.807884454727173 metric=gravl.runtime min=3.807884454727173 stddev=0
qp export
Description
Export an activity from the source
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries | ||
| from | Source data provider | ||
| overwrite | o | Overwrite the file if it exists; fail otherwise | |
| output | O | The filename to use for writing the contents of the export, | |
| if not specified the contents are streamed to stdout |
Example
If neither -o or -O are specified the contents of the file are written to stdout.
If -o is specified, the file will be written to disk using the name provided by Strava, even if it already exists locally.
If -O is specified, the file will be written to disk using the name provided by the flag. It will not overwrite an existing
file unless -o was also specified.
qp list
Description
List the files suitable for uploading
Syntax
qp providers
Description
Return the set of active exporters and uploaders
Syntax
qp status
Description
Check the status of an upload
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries | ||
| to | Sink data provider | ||
| poll | Continually check the status of the request until it is completed | ||
| interval | The amount of time to wait between polling for an updated status | ||
| iterations | N | The max number of polling iterations to perform |
qp upload
Description
Upload one or more activity files (FIT, GPX, TCX) to the specified platform
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries | ||
| to | Sink data provider | ||
| poll | Continually check the status of the request until it is completed | ||
| interval | The amount of time to wait between polling for an updated status | ||
| iterations | N | The max number of polling iterations to perform |
rwgps
Description
Operations supported by the RideWithGPS API
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| rwgps-client-id | RWGPS_CLIENT_ID | RideWithGPS client id | |
| rwgps-access-token | RWGPS_ACCESS_TOKEN | RideWithGPS access token | |
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries |
rwgps activities
Description
Query the RideWithGPS API for a list of trips for the authenticated athlete
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| count | N | The number of activities to query from RideWithGPS |
Example
Query RideWithGPS activities with an optional count. The order of the activities is not guaranteed but generally they are returned most recent first.
$ gravl rwgps activities -N 10
2021-02-20T14:31:23-08:00 INF do all=0 count=10 n=0 start=0 total=10
2021-02-20T14:31:23-08:00 INF do all=10 count=10 n=10 start=1 total=10
{
"created_at": "2021-02-20T20:12:49Z",
"departed_at": "2021-02-20T18:39:22Z",
"description": "",
"distance": 30741.9,
"duration": 5593,
"elevation_gain": 516.281,
"elevation_loss": 521.453,
"id": 62829442,
"name": "2021/02/20",
"type": "",
"track_id": "60316d406b34d7693a3f015b",
"updated_at": "2021-02-20T20:12:49Z",
"user_id": 836,
"visibility": 1,
"first_lat": 47.502655,
"first_lng": -122.602798,
"last_lat": 47.502655,
"last_lng": -122.602798
}
...
{
"created_at": "2021-01-26T00:38:58Z",
"departed_at": "2021-01-25T23:34:32Z",
"description": "",
"distance": 25356,
"duration": 3851,
"elevation_gain": 403.445,
"elevation_loss": 400.121,
"id": 61904776,
"name": "2021/01/25",
"type": "",
"track_id": "600f64a2fa348e8e23e2d546",
"updated_at": "2021-01-26T00:38:58Z",
"user_id": 836,
"visibility": 1,
"first_lat": 47.502655,
"first_lng": -122.602798,
"last_lat": 47.502655,
"last_lng": -122.602798
}
rwgps activity
Description
Query the RideWithGPS API for a specific trip by its ID
Syntax
rwgps athlete
Description
Query the RideWithGPS API for the authenticated athlete's profile and display their account information
Syntax
rwgps route
Description
Query the RideWithGPS API for a specific route by its ID
Syntax
rwgps routes
Description
Query the RideWithGPS API for a list of planned routes for the authenticated athlete
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| count | N | The number of routes to query from RideWithGPS |
strava
Description
Operations supported by the Strava API
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| strava-client-id | STRAVA_CLIENT_ID | Strava client id | |
| strava-client-secret | STRAVA_CLIENT_SECRET | Strava client secret | |
| strava-refresh-token | STRAVA_REFRESH_TOKEN | Strava refresh token | |
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries |
Overview
The Strava client is comprised of general API access supporting activities, routes, and streams.
Additionally, there's full support for implementing webhooks but only only webhook management is
available via the commandline (eg strava webhook list,
strava webhook subscribe, and strava webhook unsubscribe).
strava activities
Description
Query the Strava API for a list of activities for the authenticated athlete, with optional date range filtering and expression-based attribute extraction
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| count | N | The number of activities to query from Strava (the number returned will be <= N) | |
| filter | f | Expression for filtering activities to remove | |
| attribute | B | Evaluate the expression on an activity and return only those results | |
| after | since | Return results after the time specified | |
| before | Return results before the time specified |
Example
List all VirtualRides in the last 20 activities and display their ID, Name, StartDate, and their Distance in miles
$ gravl -c --timeout 1m strava activities -N 20 -f ".Type == 'VirtualRide'" -B ".ID, .Name, .StartDateLocal, .Distance.Miles()"
2021-02-20T08:50:32-08:00 INF do all=0 count=20 n=0 start=0 total=20
2021-02-20T08:50:34-08:00 INF do all=20 count=20 n=20 start=1 total=20
[4807285657,"Yorkshire - Jon's Short Mix","2021-02-18T06:56:20Z",10.702124592380498]
[4802094087,"London","2021-02-17T06:55:39Z",12.95105334844508]
[4741552384,"2004","2021-02-05T18:15:27Z",17.51514902966675]
strava activity
Description
Query the Strava API for a specific activity by its ID, optionally including data streams
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| stream | s | Streams to include in the activity |
Example
To query a specific activity:
$ gravl strava activity 4802094087
{
"id": 4802094087,
"resource_state": 3,
"external_id": "zwift-activity-752165469668926976.fit",
"upload_id": 5123870639,
...
"gear": {
"id": "b6713218",
"primary": false,
"name": "Smart Trainer",
"resource_state": 2,
"distance": 711026,
"athlete_id": 0
},
...
"device_name": "Zwift",
"segment_leaderboard_opt_out": false,
"leaderboard_opt_out": false,
"perceived_exertion": 6,
"prefer_perceived_exertion": false
}
To include stream data use the -s flag:
$ gravl strava activity -s watts 4802094087
{
"id": 4802094087,
"resource_state": 3,
"external_id": "zwift-activity-752165469668926976.fit",
"upload_id": 5123870639,
...
"name": "London",
"distance": 20842.7,
"moving_time": 2463,
"elapsed_time": 2463,
"total_elevation_gain": 216,
"type": "VirtualRide",
"start_date": "2021-02-17T14:55:39Z",
"start_date_local": "2021-02-17T06:55:39Z",
...
"streams": {
"activity_id": 4802094087,
"distance": {
"original_size": 2464,
"resolution": "high",
"series_type": "distance",
"data": [
2.5,
4.3,
6.6,
9.2,
...
20829.9,
20835.3,
20840.2,
20845.2
]
},
"watts": {
"original_size": 2464,
"resolution": "high",
"series_type": "distance",
"data": [
89,
105,
105,
105,
...
367,
368,
376,
408,
406,
396,
396,
408,
412,
361,
400,
...
]
}
}
}
strava athlete
Description
Query the Strava API for the authenticated athlete's profile and display their account information
Syntax
strava oauth
Description
Start a local OAuth server to acquire access and refresh tokens for the specified provider
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| origin | Callback origin | ||
| port | Port on which to listen |
strava photos
Description
Query the Strava API for the photos associated with a specific activity
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| size | s | Maximum size in pixels of the photos to return |
strava refresh
Description
Exchange the existing refresh token for a new access and refresh token pair
Syntax
strava route
Description
Query the Strava API for a specific route by its ID
Syntax
strava routes
Description
Query the Strava API for a list of planned routes for the authenticated athlete
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| count | N | The number of routes to query from Strava (the number returned will be <= N) |
strava streams
Description
Query the Strava API for the data streams of a specific activity, such as GPS coordinates, altitude, and time
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| stream | s | Streams to include in the activity |
strava streamsets
Description
Query the Strava API for the set of available data streams that can be requested for an activity
Syntax
Example
Query the available streams for query with activity.
$ gravl strava streamsets
{
"altitude": "The sequence of altitude values for this stream, in meters [float]",
"cadence": "The sequence of cadence values for this stream, in rotations per minute [integer]",
"distance": "The sequence of distance values for this stream, in meters [float]",
"grade_smooth": "The sequence of grade values for this stream, as percents of a grade [float]",
"heartrate": "The sequence of heart rate values for this stream, in beats per minute [integer]",
"latlng": "The sequence of lat/long values for this stream [float, float]",
"moving": "The sequence of moving values for this stream, as boolean values [boolean]",
"temp": "The sequence of temperature values for this stream, in celsius degrees [float]",
"time": "The sequence of time values for this stream, in seconds [integer]",
"velocity_smooth": "The sequence of velocity values for this stream, in meters per second [float]",
"watts": "The sequence of power values for this stream, in watts [integer]"
}
To get just the stream names:
$ gravl strava streamsets | jq -r "keys | .[]"
altitude
cadence
distance
grade_smooth
heartrate
latlng
moving
temp
time
velocity_smooth
watts
strava update
Description
Update attributes of a specific Strava activity such as name, sport type, gear, description, commute status, trainer status, and visibility
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| name | Set the name for the activity | ||
| gear | Set the gear id for the activity | ||
| sport | Set the sport for the activity | ||
| description | Set the description for the activity | ||
| hidden | Hide the activity from the home dashboard | ||
| no-hidden | Display the activity on the home dashboard | ||
| commute | The activity is a commute | ||
| no-commute | The activity is not a commute | ||
| trainer | The activity was completed on a trainer | ||
| no-trainer | The activity was not completed on a trainer |
strava webhook
Description
Manage Strava webhook subscriptions for receiving event notifications
strava webhook list
Description
List all active Strava webhook subscriptions for the authenticated application
Syntax
strava webhook subscribe
Description
Subscribe the application to receive Strava webhook event notifications at the specified callback URL
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| url | Address where webhook events will be sent (max length 255 characters) | ||
| verify | String chosen by the application owner for client security |
strava webhook unsubscribe
Description
Unsubscribe from Strava webhook notifications; if no subscription ID is provided, all active subscriptions will be removed
Syntax
version
Description
Display the build version, commit hash, build time, and other build information for gravl
Syntax
zwift
Description
Operations supported by the Zwift API
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| zwift-username | ZWIFT_USERNAME | Zwift username | |
| zwift-password | ZWIFT_PASSWORD | Zwift password | |
| rate-limit | Minimum time interval between API request events (eg, 1ms, 2s, 5m, 3h) | ||
| rate-burst | Maximum burst size for API request events | ||
| concurrency | Maximum concurrent API queries |
Overview
The Zwift client was heavily inspired by zwift-client.
The command files is useful for uploading local files to services without direct
integration such as Cycling Analytics.
zwift activities
Description
Query the Zwift API for a list of activities for the authenticated athlete
Syntax
Flags
| Name | Aliases | EnvVars | Description |
|---|---|---|---|
| count | N | The number of activities to query from Zwift (the number returned will be <= N) |
zwift activity
Description
Query the Zwift API for a specific activity by its ID
Syntax
zwift athlete
Description
Query the Zwift API for an athlete profile; defaults to the authenticated user if no username is specified
Syntax
zwift files
Description
List all local Zwift FIT files; if no directories are specified, defaults to the standard Zwift Activities directory
Syntax
Example
List all local files from the Zwift app's directory. Any files less than 1K in size or named inProgressActivity.fit will be ignored.
$ gravl zwift files
2021-02-19T19:39:25-08:00 WRN skipping, too small file=/Users/bzimmer/Documents/Zwift/Activities/2021-01-12-18-39-52.fit size=584
2021-02-19T19:39:25-08:00 WRN skipping, too small file=/Users/bzimmer/Documents/Zwift/Activities/2021-01-26-18-14-13.fit size=584
2021-02-19T19:39:25-08:00 WRN skipping, activity in progress file=/Users/bzimmer/Documents/Zwift/Activities/inProgressActivity.fit
[
"/Users/bzimmer/Documents/Zwift/Activities/2021-01-12-18-40-53.fit",
"/Users/bzimmer/Documents/Zwift/Activities/2021-01-26-18-15-16.fit"
]
This command can be easily combined with jq to upload files to CyclingAnalytics, Strava, or any other site.
$ gravl zwift files | jq -r ".[]" | xargs gravl strava upload -n
2021-02-19T19:41:50-08:00 WRN skipping, too small file=/Users/bzimmer/Documents/Zwift/Activities/2021-01-12-18-39-52.fit size=584
2021-02-19T19:41:50-08:00 WRN skipping, too small file=/Users/bzimmer/Documents/Zwift/Activities/2021-01-26-18-14-13.fit size=584
2021-02-19T19:41:50-08:00 WRN skipping, activity in progress file=/Users/bzimmer/Documents/Zwift/Activities/inProgressActivity.fit
2021-02-19T19:41:50-08:00 INF collecting file=/Users/bzimmer/Documents/Zwift/Activities/2021-01-12-18-40-53.fit
2021-02-19T19:41:50-08:00 INF uploading dryrun=true file=2021-01-12-18-40-53.fit
2021-02-19T19:41:50-08:00 INF collecting file=/Users/bzimmer/Documents/Zwift/Activities/2021-01-26-18-15-16.fit
2021-02-19T19:41:50-08:00 INF uploading dryrun=true file=2021-01-26-18-15-16.fit
zwift refresh
Description
Acquire a new access token by authenticating with Zwift credentials
Syntax
Example
Query for a new refresh token from Zwift.