fit-file-faker 1.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fit_file_faker-1.2.3/LICENSE.md +7 -0
- fit_file_faker-1.2.3/PKG-INFO +307 -0
- fit_file_faker-1.2.3/README.md +285 -0
- fit_file_faker-1.2.3/app.py +667 -0
- fit_file_faker-1.2.3/fit_file_faker.egg-info/PKG-INFO +307 -0
- fit_file_faker-1.2.3/fit_file_faker.egg-info/SOURCES.txt +10 -0
- fit_file_faker-1.2.3/fit_file_faker.egg-info/dependency_links.txt +1 -0
- fit_file_faker-1.2.3/fit_file_faker.egg-info/entry_points.txt +2 -0
- fit_file_faker-1.2.3/fit_file_faker.egg-info/requires.txt +7 -0
- fit_file_faker-1.2.3/fit_file_faker.egg-info/top_level.txt +1 -0
- fit_file_faker-1.2.3/pyproject.toml +43 -0
- fit_file_faker-1.2.3/setup.cfg +4 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2024, Joshua Taillon
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fit-file-faker
|
|
3
|
+
Version: 1.2.3
|
|
4
|
+
Summary: A small tool to edit and upload FIT files to Garmin Connect
|
|
5
|
+
Author-email: Josh Taillon <jat255@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/jat255/Fit-File-Faker
|
|
7
|
+
Project-URL: Issues, https://github.com/jat255/Fit-File-Faker/issues
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.12.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE.md
|
|
14
|
+
Requires-Dist: fit-tool>=0.9.13
|
|
15
|
+
Requires-Dist: garth>=0.5.2
|
|
16
|
+
Requires-Dist: platformdirs>=4.3.6
|
|
17
|
+
Requires-Dist: questionary>=2.1.0
|
|
18
|
+
Requires-Dist: rich>=13.9.4
|
|
19
|
+
Requires-Dist: semver>=3.0.2
|
|
20
|
+
Requires-Dist: watchdog>=6.0.0
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Fit File Faker - a FIT File editor and uploader
|
|
24
|
+
|
|
25
|
+
This repo contains a tool that will edit [FIT](https://developer.garmin.com/fit/overview/) files
|
|
26
|
+
to make them appear to come from a Garmin device (Edge 830, currently) and upload them to Garmin Connect
|
|
27
|
+
using the [`garth`](https://github.com/matin/garth/) library. The FIT editing
|
|
28
|
+
is done using Stages Cycling's [`fit_tool`](https://bitbucket.org/stagescycling/python_fit_tool/src/main/) library.
|
|
29
|
+
|
|
30
|
+
The primary use case for this is that [TrainingPeaks Virtual](https://www.trainingpeaks.com/virtual/) (previously
|
|
31
|
+
[indieVelo](https://indievelo.com/)) does not support (AFAIK, Garmin does not allow) automatic uploading to
|
|
32
|
+
[Garmin Connect](http://connect.garmin.com/). The files can be manually uploaded after the fact,
|
|
33
|
+
but since they are not "from Garmin", they will not be used to calculate Garmin's "Training Effect",
|
|
34
|
+
which is used for suggested workouts and other stuff. By changing the FIT file to appear to come
|
|
35
|
+
from a Garmin device, those features should be enabled.
|
|
36
|
+
|
|
37
|
+
Other users have reported using this tool to edit FIT files produced by [Zwift](https://www.zwift.com/)
|
|
38
|
+
prior to uploading to Garmin Connect so that activities on that platform will count towards Garmin Connect
|
|
39
|
+
badges and challenges (see [1](https://forums.zwift.com/t/garmin-disabled-zwift-rides-badges/528612) and
|
|
40
|
+
[2](https://forums.garmin.com/apps-software/mobile-apps-web/f/garmin-connect-web/251574/zwift-rides-no-longer-count-towards-challenges)).
|
|
41
|
+
|
|
42
|
+
## Contributors
|
|
43
|
+
|
|
44
|
+
- [jat255](https://github.com/jat255): Primary author
|
|
45
|
+
- [benjmarshall](https://github.com/benjmarshall): bug fixes, monitor mode, and other improvements
|
|
46
|
+
- [Kellett](https://github.com/Kellett): support for Zwift FIT files
|
|
47
|
+
- [lrybak](https://github.com/lrybak): support for Hammerhead Karoo files
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
Requires Python 3.12. If your system python is older than that,
|
|
52
|
+
[pyenv](https://github.com/pyenv/pyenv) or [uv](https://docs.astral.sh/uv/) can be
|
|
53
|
+
used to manage locally installed versions.
|
|
54
|
+
|
|
55
|
+
This tool should work cross-platform on Windows, MacOS, or Linux, though it is primarily
|
|
56
|
+
developed on Linux, so it's possible there are some cross-platform bugs.
|
|
57
|
+
|
|
58
|
+
### pipx install
|
|
59
|
+
|
|
60
|
+
If you have [pipx](https://pipx.pypa.io/latest/installation/) installed, a simple
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
$ pipx install fit-file-faker
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
will install the tool, and make the script named `fit-file-faker` available on your PATH.
|
|
67
|
+
|
|
68
|
+
### Manual virtual environment
|
|
69
|
+
|
|
70
|
+
You can also install manually using `pip`. If so, it's best to create a new
|
|
71
|
+
virtual environment:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
$ python -m venv .venv
|
|
75
|
+
$ source .venv/bin/activate
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then install via pip:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
$ pip install fit-file-faker
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The pip package installs a script named `fit-file-faker` that should be available on your
|
|
85
|
+
path assuming the virtual envrionment is activated.
|
|
86
|
+
|
|
87
|
+
### Development install
|
|
88
|
+
|
|
89
|
+
If you want to install a development version, clone the repo, and use the
|
|
90
|
+
[uv](https://docs.astral.sh/uv/):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
$ git clone https://github.com/jat255/fit_file_uploader.git
|
|
94
|
+
$ cd fit_file_uploader
|
|
95
|
+
$ uv sync # this installs the dependencies
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Configuration
|
|
99
|
+
|
|
100
|
+
The script uses a configuration file named `.config.json` stored in your system's user config directory
|
|
101
|
+
(as determined by the [`platformdirs`](https://github.com/tox-dev/platformdirs) library).
|
|
102
|
+
An example is provided in this repo in `.config.json.example`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"garmin_username": "username",
|
|
107
|
+
"garmin_password": "password",
|
|
108
|
+
"fitfiles_path": "C:\\Users\\username\\Documents\\TPVirtual\\0123456789ABCDEF\\FITFiles"
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The best way to fill out this config file is to run the "initial setup"
|
|
113
|
+
option via the `-s` flag, which will allow you to define the three required values interactively:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
$ fit-file-faker -s
|
|
117
|
+
|
|
118
|
+
[13:50:02] WARNING Required value "garmin_username" not found in config app.py:404
|
|
119
|
+
? Enter value to use for "garmin_username" username
|
|
120
|
+
[13:50:05] WARNING Required value "garmin_password" not found in config app.py:404
|
|
121
|
+
? Enter value to use for "garmin_password" ********
|
|
122
|
+
[13:50:06] WARNING Required value "fitfiles_path" not found in config app.py:404
|
|
123
|
+
INFO Getting FITFiles folder app.py:133
|
|
124
|
+
WARNING TrainingPeaks Virtual user folder can only be automatically app.py:175
|
|
125
|
+
detected on Windows and OSX
|
|
126
|
+
? Please enter your TrainingPeaks Virtual data folder (by default, ends with "TPVirtual"):
|
|
127
|
+
/home/user/Documents/TPVirtual
|
|
128
|
+
? Found TP Virtual User directory at "/home/user/Documents/TPVirtual/0123456789ABCDEF", is this correct?
|
|
129
|
+
yes
|
|
130
|
+
[13:50:17] INFO Found TP Virtual User directory: "/home/user/Documents/TPVirtual app.py:158
|
|
131
|
+
sync/0123456789ABCEDF", setting "fitfiles_path" in config file
|
|
132
|
+
INFO Config file is now: app.py:440
|
|
133
|
+
{
|
|
134
|
+
"garmin_username": "username",
|
|
135
|
+
"garmin_password": "<**hidden**>",
|
|
136
|
+
"fitfiles_path": "/home/user/Documents/TPVirtual/0123456789ABCDEF/FITFiles"
|
|
137
|
+
}
|
|
138
|
+
INFO Config file has been written, now run one of the other options app.py:530
|
|
139
|
+
to start editing/uploading files!
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Usage
|
|
143
|
+
|
|
144
|
+
The script has a few options. To see the help, run with the `-h` flag:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
$ fit-file-faker -h
|
|
148
|
+
```
|
|
149
|
+
```
|
|
150
|
+
usage: fit-file-faker [-h] [-s] [-u] [-ua] [-p] [-m] [--dryrun] [-v] [input_path]
|
|
151
|
+
|
|
152
|
+
Tool to add Garmin device information to FIT files and upload them to Garmin Connect. Currently,
|
|
153
|
+
only FIT files produced by TrainingPeaks Virtual (https://www.trainingpeaks.com/virtual/) and
|
|
154
|
+
Zwift (https://www.zwift.com/) are supported, but it's possible others may work.
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
positional arguments:
|
|
158
|
+
input_path the FIT file or directory to process. This argument can be omitted if
|
|
159
|
+
the 'fitfiles_path' config value is set (that directory will be used
|
|
160
|
+
instead). By default, files will just be edited. Specify the "-u" flag
|
|
161
|
+
to also upload them to Garmin Connect.
|
|
162
|
+
|
|
163
|
+
options:
|
|
164
|
+
-h, --help show this help message and exit
|
|
165
|
+
-s, --initial-setup Use this option to interactively initialize the configuration file
|
|
166
|
+
(.config.json)
|
|
167
|
+
-u, --upload upload FIT file (after editing) to Garmin Connect
|
|
168
|
+
-ua, --upload-all upload all FIT files in directory (if they are not in "already
|
|
169
|
+
processed" list)
|
|
170
|
+
-p, --preinitialize preinitialize the list of processed FIT files (mark all existing files
|
|
171
|
+
in directory as already uploaded)
|
|
172
|
+
-m, --monitor monitor a directory and upload all newly created FIT files as they are
|
|
173
|
+
found
|
|
174
|
+
-d, --dryrun perform a dry run, meaning any files processed will not be saved nor
|
|
175
|
+
uploaded
|
|
176
|
+
-v, --verbose increase verbosity of log output
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Basic usage
|
|
180
|
+
|
|
181
|
+
The default behavior with no other options load a given FIT file, and output a file named `path_to_file_modified.fit`
|
|
182
|
+
that has been edited, and can be manually imported to Garmin Connect:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
$ fit-file-faker path_to_file.fit
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
If a directory is supplied rather than a single file, all FIT files in that directory will be processed in
|
|
189
|
+
the same way.
|
|
190
|
+
|
|
191
|
+
Supplying the `-u` option will attempt to upload the edited file to Garmin Connect. If
|
|
192
|
+
your credentials are not stored in the configuration file, the script will prompt you for them.
|
|
193
|
+
The OAuth credentials obtained for the Garmin web service will be stored in a directory
|
|
194
|
+
named `.garth` in your system's user cache folder (as determined by
|
|
195
|
+
[`platformdirs`](https://github.com/tox-dev/platformdirs)). See the `garth` library's
|
|
196
|
+
[documentation](https://github.com/matin/garth/?tab=readme-ov-file#authentication-and-stability)
|
|
197
|
+
for details:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
$ fit-file-faker -u path_to_file.fit
|
|
201
|
+
```
|
|
202
|
+
```
|
|
203
|
+
[12:14:06] INFO Activity timestamp is "2024-05-21T17:15:48" app.py:84
|
|
204
|
+
INFO Saving modified data to path_to_file_modified.fit app.py:106
|
|
205
|
+
[12:14:08] INFO ✅ Successfully uploaded "path_to_file.fit" app.py:137
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The `-v` flag can be used (with any of the other options) to provide more debugging output:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
$ fit-file-faker -u path_to_file.fit -v
|
|
212
|
+
```
|
|
213
|
+
```
|
|
214
|
+
[12:38:33] INFO Activity timestamp is "2024-05-21T17:15:48" app.py:84
|
|
215
|
+
DEBUG Record: 1 - manufacturer: 255 ("DEVELOPMENT") - product: 0 - garmin app.py:55
|
|
216
|
+
product: None ("BLANK")
|
|
217
|
+
DEBUG Modifying values app.py:87
|
|
218
|
+
DEBUG New Record: 1 - manufacturer: 1 ("GARMIN") - product: 3122 - garmin app.py:55
|
|
219
|
+
product: 3122 ("GarminProduct.EDGE_830")
|
|
220
|
+
DEBUG Record: 14 - manufacturer: 32 ("WAHOO_FITNESS") - product: 40 - garmin app.py:55
|
|
221
|
+
product: None ("BLANK")
|
|
222
|
+
DEBUG Modifying values app.py:97
|
|
223
|
+
DEBUG New Record: 14 - manufacturer: 1 ("GARMIN") - product: 3122 - garmin app.py:55
|
|
224
|
+
product: 3122 ("GarminProduct.EDGE_830")
|
|
225
|
+
DEBUG Record: 15 - manufacturer: 32 ("WAHOO_FITNESS") - product: 6 - garmin app.py:55
|
|
226
|
+
product: None ("BLANK")
|
|
227
|
+
DEBUG Modifying values app.py:97
|
|
228
|
+
DEBUG New Record: 15 - manufacturer: 1 ("GARMIN") - product: 3122 - garmin app.py:55
|
|
229
|
+
product: 3122 ("GarminProduct.EDGE_830")
|
|
230
|
+
DEBUG Record: 16 - manufacturer: 1 ("GARMIN") - product: 18 - garmin product: app.py:55
|
|
231
|
+
18 ("BLANK")
|
|
232
|
+
INFO Saving modified data to app.py:106
|
|
233
|
+
"path_to_file_modified.fit"
|
|
234
|
+
[12:38:34] DEBUG Using stored Garmin credentials from ".garth" directory app.py:118
|
|
235
|
+
[12:38:35] INFO ✅ Successfully uploaded "path_to_file.fit" app.py:137
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### "Upload all" and "monitor" modes
|
|
239
|
+
|
|
240
|
+
The `--upload-all` option will search for all FIT files eith in the directory given on the command line,
|
|
241
|
+
or in the one specified in the `fitfiles_path` config option. The script will compare the files found to a
|
|
242
|
+
list of files already seen (stored in that directory's `.uploaded_files.json` file), edit them, and upload
|
|
243
|
+
each to Garmin Connect. The edited files will be written into a temporary file and discarded when the
|
|
244
|
+
script finishes running, and the filenames will be stored into a JSON file in the current directory so
|
|
245
|
+
they are skipped the next time the script is run.
|
|
246
|
+
|
|
247
|
+
The upload all function can alternatively be automated using the `--monitor` option, which will start
|
|
248
|
+
watching the filesystem in the specified directory for any new FIT files, and continue running until
|
|
249
|
+
the user interrupts the process by pressing `ctrl-c`. Here is an example output when a new file named
|
|
250
|
+
`new_fit_file.fit` is detected:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
$ fit-file-faker --monitor /home/user/Documents/TPVirtual/0123456789ABCEDF/FITFiles
|
|
254
|
+
|
|
255
|
+
[14:03:32] INFO Using path "/home/user/Documents/TPVirtual/ app.py:561
|
|
256
|
+
0123456789ABCEDF/FITFiles" from command line input
|
|
257
|
+
INFO Monitoring directory: "/home/user/Documents/TPVirtual/ app.py:367
|
|
258
|
+
0123456789ABCEDF/FITFiles"
|
|
259
|
+
[14:03:44] INFO New file detected - "/home/user/Documents/TPVirtual/ app.py:94
|
|
260
|
+
0123456789ABCEDF/FITFiles/new_fit_file.fit"; sleeping for
|
|
261
|
+
5 seconds to ensure TPV finishes writing file
|
|
262
|
+
[14:03:50] INFO Found 1 files to edit/upload app.py:333
|
|
263
|
+
INFO Processing "new_fit_file.fit" app.py:340
|
|
264
|
+
INFO Processing "/home/user/Documents/TPVirtual app.py:202
|
|
265
|
+
sync/0123456789ABCEDF/FITFiles/new_fit_file.fit"
|
|
266
|
+
[14:03:58] INFO Activity timestamp is "2025-01-03T17:01:45" app.py:223
|
|
267
|
+
[14:03:59] INFO Saving modified data to "/tmp/tmpsn4gvpkh" app.py:250
|
|
268
|
+
[14:04:00] INFO Uploading modified file to Garmin Connect app.py:346
|
|
269
|
+
[14:04:01] INFO Uploading "/tmp/tmpsn4gvpkh" using garth app.py:295
|
|
270
|
+
^C[14:04:46] INFO Received keyboard interrupt, shutting down monitor app.py:372
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
If your TrainingPeaks Virtual user data folder already contains FIT files which you have previously uploaded
|
|
274
|
+
to Garmin Connect using a different method then you can pre-initialise the list of uploaded files to avoid
|
|
275
|
+
any possibility of uploading duplicates (though these files *should* be rejected by Garmin Connect
|
|
276
|
+
if they're exact duplicates). Use the `--preinitialize` option to process a directory (defaults to
|
|
277
|
+
the configured TrainingPeaks Virtual user data directory) and add all files to the list of previous uploaded
|
|
278
|
+
files. After this any use of the `--upload-all` or `--monitor` options will ignore these pre-existing files.
|
|
279
|
+
|
|
280
|
+
### Already uploaded files
|
|
281
|
+
|
|
282
|
+
A note: if a file with the same timestamp already exists on the Garmin Connect account, Garmin
|
|
283
|
+
will reject the upload. This script will detect that, and output something like the following:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
$ fit-file-faker -u path_to_file.fit -v
|
|
287
|
+
```
|
|
288
|
+
```
|
|
289
|
+
[13:32:48] INFO Activity timestamp is "2024-05-10T17:17:34" app.py:85
|
|
290
|
+
INFO Saving modified data to "path_to_file_modified.fit" app.py:107
|
|
291
|
+
[13:32:49] WARNING ❌ Received HTTP conflict (activity already exists) for app.py:143
|
|
292
|
+
"path_to_file.fit"
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Troubleshooting
|
|
296
|
+
|
|
297
|
+
If you run into problems, please
|
|
298
|
+
[create an issue](https://github.com/jat255/fit_file_uploader/issues/new/choose) on the GitHub
|
|
299
|
+
repo. As this is a side-project provided for free (as in speech and beer), support times may vary 😅.
|
|
300
|
+
|
|
301
|
+
## Disclaimer
|
|
302
|
+
|
|
303
|
+
The use of any registered or unregistered trademarks owned by third-parties are used only for
|
|
304
|
+
informational purposes and no endorsement of this software by the owners of such trademarks are
|
|
305
|
+
implied, explicitly or otherwise. The terms/trademarks indieVelo, TrainingPeaks, TrainingPeaks Virtual,
|
|
306
|
+
Garmin Connect, Stages Cycling, and any others are used under fair use doctrine solely to
|
|
307
|
+
facilitate understanding.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Fit File Faker - a FIT File editor and uploader
|
|
2
|
+
|
|
3
|
+
This repo contains a tool that will edit [FIT](https://developer.garmin.com/fit/overview/) files
|
|
4
|
+
to make them appear to come from a Garmin device (Edge 830, currently) and upload them to Garmin Connect
|
|
5
|
+
using the [`garth`](https://github.com/matin/garth/) library. The FIT editing
|
|
6
|
+
is done using Stages Cycling's [`fit_tool`](https://bitbucket.org/stagescycling/python_fit_tool/src/main/) library.
|
|
7
|
+
|
|
8
|
+
The primary use case for this is that [TrainingPeaks Virtual](https://www.trainingpeaks.com/virtual/) (previously
|
|
9
|
+
[indieVelo](https://indievelo.com/)) does not support (AFAIK, Garmin does not allow) automatic uploading to
|
|
10
|
+
[Garmin Connect](http://connect.garmin.com/). The files can be manually uploaded after the fact,
|
|
11
|
+
but since they are not "from Garmin", they will not be used to calculate Garmin's "Training Effect",
|
|
12
|
+
which is used for suggested workouts and other stuff. By changing the FIT file to appear to come
|
|
13
|
+
from a Garmin device, those features should be enabled.
|
|
14
|
+
|
|
15
|
+
Other users have reported using this tool to edit FIT files produced by [Zwift](https://www.zwift.com/)
|
|
16
|
+
prior to uploading to Garmin Connect so that activities on that platform will count towards Garmin Connect
|
|
17
|
+
badges and challenges (see [1](https://forums.zwift.com/t/garmin-disabled-zwift-rides-badges/528612) and
|
|
18
|
+
[2](https://forums.garmin.com/apps-software/mobile-apps-web/f/garmin-connect-web/251574/zwift-rides-no-longer-count-towards-challenges)).
|
|
19
|
+
|
|
20
|
+
## Contributors
|
|
21
|
+
|
|
22
|
+
- [jat255](https://github.com/jat255): Primary author
|
|
23
|
+
- [benjmarshall](https://github.com/benjmarshall): bug fixes, monitor mode, and other improvements
|
|
24
|
+
- [Kellett](https://github.com/Kellett): support for Zwift FIT files
|
|
25
|
+
- [lrybak](https://github.com/lrybak): support for Hammerhead Karoo files
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Requires Python 3.12. If your system python is older than that,
|
|
30
|
+
[pyenv](https://github.com/pyenv/pyenv) or [uv](https://docs.astral.sh/uv/) can be
|
|
31
|
+
used to manage locally installed versions.
|
|
32
|
+
|
|
33
|
+
This tool should work cross-platform on Windows, MacOS, or Linux, though it is primarily
|
|
34
|
+
developed on Linux, so it's possible there are some cross-platform bugs.
|
|
35
|
+
|
|
36
|
+
### pipx install
|
|
37
|
+
|
|
38
|
+
If you have [pipx](https://pipx.pypa.io/latest/installation/) installed, a simple
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
$ pipx install fit-file-faker
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
will install the tool, and make the script named `fit-file-faker` available on your PATH.
|
|
45
|
+
|
|
46
|
+
### Manual virtual environment
|
|
47
|
+
|
|
48
|
+
You can also install manually using `pip`. If so, it's best to create a new
|
|
49
|
+
virtual environment:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
$ python -m venv .venv
|
|
53
|
+
$ source .venv/bin/activate
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then install via pip:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
$ pip install fit-file-faker
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The pip package installs a script named `fit-file-faker` that should be available on your
|
|
63
|
+
path assuming the virtual envrionment is activated.
|
|
64
|
+
|
|
65
|
+
### Development install
|
|
66
|
+
|
|
67
|
+
If you want to install a development version, clone the repo, and use the
|
|
68
|
+
[uv](https://docs.astral.sh/uv/):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
$ git clone https://github.com/jat255/fit_file_uploader.git
|
|
72
|
+
$ cd fit_file_uploader
|
|
73
|
+
$ uv sync # this installs the dependencies
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
The script uses a configuration file named `.config.json` stored in your system's user config directory
|
|
79
|
+
(as determined by the [`platformdirs`](https://github.com/tox-dev/platformdirs) library).
|
|
80
|
+
An example is provided in this repo in `.config.json.example`:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"garmin_username": "username",
|
|
85
|
+
"garmin_password": "password",
|
|
86
|
+
"fitfiles_path": "C:\\Users\\username\\Documents\\TPVirtual\\0123456789ABCDEF\\FITFiles"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The best way to fill out this config file is to run the "initial setup"
|
|
91
|
+
option via the `-s` flag, which will allow you to define the three required values interactively:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
$ fit-file-faker -s
|
|
95
|
+
|
|
96
|
+
[13:50:02] WARNING Required value "garmin_username" not found in config app.py:404
|
|
97
|
+
? Enter value to use for "garmin_username" username
|
|
98
|
+
[13:50:05] WARNING Required value "garmin_password" not found in config app.py:404
|
|
99
|
+
? Enter value to use for "garmin_password" ********
|
|
100
|
+
[13:50:06] WARNING Required value "fitfiles_path" not found in config app.py:404
|
|
101
|
+
INFO Getting FITFiles folder app.py:133
|
|
102
|
+
WARNING TrainingPeaks Virtual user folder can only be automatically app.py:175
|
|
103
|
+
detected on Windows and OSX
|
|
104
|
+
? Please enter your TrainingPeaks Virtual data folder (by default, ends with "TPVirtual"):
|
|
105
|
+
/home/user/Documents/TPVirtual
|
|
106
|
+
? Found TP Virtual User directory at "/home/user/Documents/TPVirtual/0123456789ABCDEF", is this correct?
|
|
107
|
+
yes
|
|
108
|
+
[13:50:17] INFO Found TP Virtual User directory: "/home/user/Documents/TPVirtual app.py:158
|
|
109
|
+
sync/0123456789ABCEDF", setting "fitfiles_path" in config file
|
|
110
|
+
INFO Config file is now: app.py:440
|
|
111
|
+
{
|
|
112
|
+
"garmin_username": "username",
|
|
113
|
+
"garmin_password": "<**hidden**>",
|
|
114
|
+
"fitfiles_path": "/home/user/Documents/TPVirtual/0123456789ABCDEF/FITFiles"
|
|
115
|
+
}
|
|
116
|
+
INFO Config file has been written, now run one of the other options app.py:530
|
|
117
|
+
to start editing/uploading files!
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Usage
|
|
121
|
+
|
|
122
|
+
The script has a few options. To see the help, run with the `-h` flag:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
$ fit-file-faker -h
|
|
126
|
+
```
|
|
127
|
+
```
|
|
128
|
+
usage: fit-file-faker [-h] [-s] [-u] [-ua] [-p] [-m] [--dryrun] [-v] [input_path]
|
|
129
|
+
|
|
130
|
+
Tool to add Garmin device information to FIT files and upload them to Garmin Connect. Currently,
|
|
131
|
+
only FIT files produced by TrainingPeaks Virtual (https://www.trainingpeaks.com/virtual/) and
|
|
132
|
+
Zwift (https://www.zwift.com/) are supported, but it's possible others may work.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
positional arguments:
|
|
136
|
+
input_path the FIT file or directory to process. This argument can be omitted if
|
|
137
|
+
the 'fitfiles_path' config value is set (that directory will be used
|
|
138
|
+
instead). By default, files will just be edited. Specify the "-u" flag
|
|
139
|
+
to also upload them to Garmin Connect.
|
|
140
|
+
|
|
141
|
+
options:
|
|
142
|
+
-h, --help show this help message and exit
|
|
143
|
+
-s, --initial-setup Use this option to interactively initialize the configuration file
|
|
144
|
+
(.config.json)
|
|
145
|
+
-u, --upload upload FIT file (after editing) to Garmin Connect
|
|
146
|
+
-ua, --upload-all upload all FIT files in directory (if they are not in "already
|
|
147
|
+
processed" list)
|
|
148
|
+
-p, --preinitialize preinitialize the list of processed FIT files (mark all existing files
|
|
149
|
+
in directory as already uploaded)
|
|
150
|
+
-m, --monitor monitor a directory and upload all newly created FIT files as they are
|
|
151
|
+
found
|
|
152
|
+
-d, --dryrun perform a dry run, meaning any files processed will not be saved nor
|
|
153
|
+
uploaded
|
|
154
|
+
-v, --verbose increase verbosity of log output
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Basic usage
|
|
158
|
+
|
|
159
|
+
The default behavior with no other options load a given FIT file, and output a file named `path_to_file_modified.fit`
|
|
160
|
+
that has been edited, and can be manually imported to Garmin Connect:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
$ fit-file-faker path_to_file.fit
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
If a directory is supplied rather than a single file, all FIT files in that directory will be processed in
|
|
167
|
+
the same way.
|
|
168
|
+
|
|
169
|
+
Supplying the `-u` option will attempt to upload the edited file to Garmin Connect. If
|
|
170
|
+
your credentials are not stored in the configuration file, the script will prompt you for them.
|
|
171
|
+
The OAuth credentials obtained for the Garmin web service will be stored in a directory
|
|
172
|
+
named `.garth` in your system's user cache folder (as determined by
|
|
173
|
+
[`platformdirs`](https://github.com/tox-dev/platformdirs)). See the `garth` library's
|
|
174
|
+
[documentation](https://github.com/matin/garth/?tab=readme-ov-file#authentication-and-stability)
|
|
175
|
+
for details:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
$ fit-file-faker -u path_to_file.fit
|
|
179
|
+
```
|
|
180
|
+
```
|
|
181
|
+
[12:14:06] INFO Activity timestamp is "2024-05-21T17:15:48" app.py:84
|
|
182
|
+
INFO Saving modified data to path_to_file_modified.fit app.py:106
|
|
183
|
+
[12:14:08] INFO ✅ Successfully uploaded "path_to_file.fit" app.py:137
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The `-v` flag can be used (with any of the other options) to provide more debugging output:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
$ fit-file-faker -u path_to_file.fit -v
|
|
190
|
+
```
|
|
191
|
+
```
|
|
192
|
+
[12:38:33] INFO Activity timestamp is "2024-05-21T17:15:48" app.py:84
|
|
193
|
+
DEBUG Record: 1 - manufacturer: 255 ("DEVELOPMENT") - product: 0 - garmin app.py:55
|
|
194
|
+
product: None ("BLANK")
|
|
195
|
+
DEBUG Modifying values app.py:87
|
|
196
|
+
DEBUG New Record: 1 - manufacturer: 1 ("GARMIN") - product: 3122 - garmin app.py:55
|
|
197
|
+
product: 3122 ("GarminProduct.EDGE_830")
|
|
198
|
+
DEBUG Record: 14 - manufacturer: 32 ("WAHOO_FITNESS") - product: 40 - garmin app.py:55
|
|
199
|
+
product: None ("BLANK")
|
|
200
|
+
DEBUG Modifying values app.py:97
|
|
201
|
+
DEBUG New Record: 14 - manufacturer: 1 ("GARMIN") - product: 3122 - garmin app.py:55
|
|
202
|
+
product: 3122 ("GarminProduct.EDGE_830")
|
|
203
|
+
DEBUG Record: 15 - manufacturer: 32 ("WAHOO_FITNESS") - product: 6 - garmin app.py:55
|
|
204
|
+
product: None ("BLANK")
|
|
205
|
+
DEBUG Modifying values app.py:97
|
|
206
|
+
DEBUG New Record: 15 - manufacturer: 1 ("GARMIN") - product: 3122 - garmin app.py:55
|
|
207
|
+
product: 3122 ("GarminProduct.EDGE_830")
|
|
208
|
+
DEBUG Record: 16 - manufacturer: 1 ("GARMIN") - product: 18 - garmin product: app.py:55
|
|
209
|
+
18 ("BLANK")
|
|
210
|
+
INFO Saving modified data to app.py:106
|
|
211
|
+
"path_to_file_modified.fit"
|
|
212
|
+
[12:38:34] DEBUG Using stored Garmin credentials from ".garth" directory app.py:118
|
|
213
|
+
[12:38:35] INFO ✅ Successfully uploaded "path_to_file.fit" app.py:137
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### "Upload all" and "monitor" modes
|
|
217
|
+
|
|
218
|
+
The `--upload-all` option will search for all FIT files eith in the directory given on the command line,
|
|
219
|
+
or in the one specified in the `fitfiles_path` config option. The script will compare the files found to a
|
|
220
|
+
list of files already seen (stored in that directory's `.uploaded_files.json` file), edit them, and upload
|
|
221
|
+
each to Garmin Connect. The edited files will be written into a temporary file and discarded when the
|
|
222
|
+
script finishes running, and the filenames will be stored into a JSON file in the current directory so
|
|
223
|
+
they are skipped the next time the script is run.
|
|
224
|
+
|
|
225
|
+
The upload all function can alternatively be automated using the `--monitor` option, which will start
|
|
226
|
+
watching the filesystem in the specified directory for any new FIT files, and continue running until
|
|
227
|
+
the user interrupts the process by pressing `ctrl-c`. Here is an example output when a new file named
|
|
228
|
+
`new_fit_file.fit` is detected:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
$ fit-file-faker --monitor /home/user/Documents/TPVirtual/0123456789ABCEDF/FITFiles
|
|
232
|
+
|
|
233
|
+
[14:03:32] INFO Using path "/home/user/Documents/TPVirtual/ app.py:561
|
|
234
|
+
0123456789ABCEDF/FITFiles" from command line input
|
|
235
|
+
INFO Monitoring directory: "/home/user/Documents/TPVirtual/ app.py:367
|
|
236
|
+
0123456789ABCEDF/FITFiles"
|
|
237
|
+
[14:03:44] INFO New file detected - "/home/user/Documents/TPVirtual/ app.py:94
|
|
238
|
+
0123456789ABCEDF/FITFiles/new_fit_file.fit"; sleeping for
|
|
239
|
+
5 seconds to ensure TPV finishes writing file
|
|
240
|
+
[14:03:50] INFO Found 1 files to edit/upload app.py:333
|
|
241
|
+
INFO Processing "new_fit_file.fit" app.py:340
|
|
242
|
+
INFO Processing "/home/user/Documents/TPVirtual app.py:202
|
|
243
|
+
sync/0123456789ABCEDF/FITFiles/new_fit_file.fit"
|
|
244
|
+
[14:03:58] INFO Activity timestamp is "2025-01-03T17:01:45" app.py:223
|
|
245
|
+
[14:03:59] INFO Saving modified data to "/tmp/tmpsn4gvpkh" app.py:250
|
|
246
|
+
[14:04:00] INFO Uploading modified file to Garmin Connect app.py:346
|
|
247
|
+
[14:04:01] INFO Uploading "/tmp/tmpsn4gvpkh" using garth app.py:295
|
|
248
|
+
^C[14:04:46] INFO Received keyboard interrupt, shutting down monitor app.py:372
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
If your TrainingPeaks Virtual user data folder already contains FIT files which you have previously uploaded
|
|
252
|
+
to Garmin Connect using a different method then you can pre-initialise the list of uploaded files to avoid
|
|
253
|
+
any possibility of uploading duplicates (though these files *should* be rejected by Garmin Connect
|
|
254
|
+
if they're exact duplicates). Use the `--preinitialize` option to process a directory (defaults to
|
|
255
|
+
the configured TrainingPeaks Virtual user data directory) and add all files to the list of previous uploaded
|
|
256
|
+
files. After this any use of the `--upload-all` or `--monitor` options will ignore these pre-existing files.
|
|
257
|
+
|
|
258
|
+
### Already uploaded files
|
|
259
|
+
|
|
260
|
+
A note: if a file with the same timestamp already exists on the Garmin Connect account, Garmin
|
|
261
|
+
will reject the upload. This script will detect that, and output something like the following:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
$ fit-file-faker -u path_to_file.fit -v
|
|
265
|
+
```
|
|
266
|
+
```
|
|
267
|
+
[13:32:48] INFO Activity timestamp is "2024-05-10T17:17:34" app.py:85
|
|
268
|
+
INFO Saving modified data to "path_to_file_modified.fit" app.py:107
|
|
269
|
+
[13:32:49] WARNING ❌ Received HTTP conflict (activity already exists) for app.py:143
|
|
270
|
+
"path_to_file.fit"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Troubleshooting
|
|
274
|
+
|
|
275
|
+
If you run into problems, please
|
|
276
|
+
[create an issue](https://github.com/jat255/fit_file_uploader/issues/new/choose) on the GitHub
|
|
277
|
+
repo. As this is a side-project provided for free (as in speech and beer), support times may vary 😅.
|
|
278
|
+
|
|
279
|
+
## Disclaimer
|
|
280
|
+
|
|
281
|
+
The use of any registered or unregistered trademarks owned by third-parties are used only for
|
|
282
|
+
informational purposes and no endorsement of this software by the owners of such trademarks are
|
|
283
|
+
implied, explicitly or otherwise. The terms/trademarks indieVelo, TrainingPeaks, TrainingPeaks Virtual,
|
|
284
|
+
Garmin Connect, Stages Cycling, and any others are used under fair use doctrine solely to
|
|
285
|
+
facilitate understanding.
|