Skip to main content

A tool for synchronisation of Withings (ex. Nokia Health Body) to Garmin Connect and Trainer Road.

Project description

[!CAUTION] This Release introduces breaking changes that users need to be aware of before upgrading or using it. These changes were made to enhance security and compatibility but may require modifications to your existing setup.

  • The container now runs without root privileges.
  • Dependencies, virtual envs, packaging is now done by Poetry.
  • This fork requires a recent version of Python, currently capped at >= python 3.12.

Make sure to go over the updated readme and test these new changes thoroughly for your environment. Chances are quite high you will have to make changes to make this work again.

withings-sync

A tool for synchronisation of the Withings API to:

  • Garmin Connect
  • Trainer Road
  • raw JSON output

1. Installation Instructions

1.1 Installation of withings-sync with pip

This method installs the package asuming you have a working python and pip installation. It relies on an external scheduler (e.g., cron on the host operating system) to manage job execution.

Expand to show installation steps.

1. installing the package:

$ pip install withings-sync

2. obtaining Withings authorization:

When running for a very first time, you need to obtain Withings authorization:

$ withings-sync
2024-12-01 01:29:02,601 - withings - ERROR - Can\'t read config file /home/youruser/.withings_user.json
2024-12-01 01:29:02,602 - withings - WARNING - User interaction needed to get Authentification Code from Withings!
2024-12-01 01:29:02,603 - withings - WARNING -
2024-12-01 01:29:02,603 - withings - WARNING - Open the following URL in your web browser and copy back the token. You will have *30 seconds* before the token expires. HURRY UP!
2024-12-01 01:29:02,603 - withings - WARNING - (This is one-time activity)
2024-12-01 01:29:02,604 - withings - WARNING -
2024-12-01 01:29:02,604 - withings - INFO - https://account.withings.com/oauth2_user/authorize2?response_type=code&client_id=183e03e1f363110b3551f96765c98c10e8f1aa647a37067a1cb64bbbaf491626&state=OK&scope=user.metrics&redirect_uri=https://jaroslawhartman.github.io/withings-sync/contrib/withings.html&
2024-12-01 01:29:02,604 - withings - INFO -

Token : <PASTE TOKEN>

2024-12-01 01:31:07,832 - withings - INFO - Get Access Token
2024-12-01 01:31:08,313 - withings - INFO - Refresh Access Token
2024-12-01 01:31:08,771 - root - INFO - Fetching measurements from 2024-12-01 00:00 to 2024-12-01 23:59
2024-12-01 01:31:09,406 - withings - INFO - Get Measurements
2024-12-01 01:31:09,856 - root - ERROR - No measurements to upload for date or period specified

You need to visit the URL listed by the script and then - copy Authentification Code back to the prompt.

This is one-time activity and it will not be needed to repeat.

3. running the application:

Subsequent runs will use the saved access tokens in ~/.withings_user.json

$ withings-sync
2024-12-01 01:37:41,500 - withings - INFO - Refresh Access Token
2024-12-01 01:37:41,954 - root - INFO - Fetching measurements from 2024-12-01 00:00 to 2024-12-01 23:59
2024-12-01 01:37:42,563 - withings - INFO - Get Measurements
2024-12-01 01:37:43,069 - root - ERROR - No measurements to upload for date or period specified

1.2 Installation of withings-sync with docker compose (not using cron)

This method follows a default approach of utilizing a single container to run one job at a time, then exiting upon completion. It relies on an external scheduler (e.g., cron on the host operating system) to manage job execution.

Expand to show installation steps.

1. create the following file/directory structure:

.                                          # STACK_PATH
./.env                                     # .env file containing your variables
./docker-compose.yml                       # docker-compose file
./config/withings-sync/                    # config directory for withings-sync (will be created automatically)

2. contents of an example .env file:

TZ=Europe/Kyiv
STACK_PATH=/home/your_user/your_stack_name
GARMIN_USERNAME="your.name@domain.ext"
GARMIN_PASSWORD="YourPasswordHere"

3. contents of an example docker-compose.yml file (simplified with --config-folder):

services:
  withings-sync:
    image: ghcr.io/jaroslawhartman/withings-sync:latest
    container_name: withings-sync
    stdin_open: true # docker run -i
    tty: true        # docker run -t
    environment:
      - TZ=${TZ:?err}
      - GARMIN_USERNAME=${GARMIN_USERNAME:?err}
      - GARMIN_PASSWORD=${GARMIN_PASSWORD:?err}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${STACK_PATH:?err}/config/withings-sync:/config
    command: ["--config-folder", "/config"]
    restart: unless-stopped

Note: Using --config-folder simplifies the setup by:

  • Storing both Withings and Garmin session files in one directory
  • No need to mount individual files
  • Easier backup and migration of configuration

4. obtaining Withings authorization:

$ docker compose pull
[+] Pulling 13/13
✔ withings-sync Pulled                                                           56.0s
   cb8611c9fe51 Pull complete                                                    4.2s
   52e189a1282f Pull complete                                                    6.4s
   95e68cb0cebc Pull complete                                                   19.0s
   c3ba8bc06a4d Pull complete                                                   19.3s
   fc2b9c85008a Pull complete                                                   21.6s
   0376fca350d9 Pull complete                                                   21.7s
   4f4fb700ef54 Pull complete                                                   21.9s
   c749d618f51d Pull complete                                                   43.0s
   86d00088bd8d Pull complete                                                   43.2s
   98dec7b84387 Pull complete                                                   52.8s
   8825309bd8c2 Pull complete                                                   53.1s
   7747652082d6 Pull complete                                                   53.3s

First start to ensure the script can start successfully:

$ docker compose run -it --remove-orphans --entrypoint "poetry run withings-sync" withings-sync
[+] Creating 1/1
✔ Network stack_default  Created                                                  0.5s
2024-12-01 01:29:02,601 - withings - ERROR - Can\'t read config file /home/youruser/.withings_user.json
2024-12-01 01:29:02,602 - withings - WARNING - User interaction needed to get Authentification Code from Withings!
2024-12-01 01:29:02,603 - withings - WARNING -
2024-12-01 01:29:02,603 - withings - WARNING - Open the following URL in your web browser and copy back the token. You will have *30 seconds* before the token expires. HURRY UP!
2024-12-01 01:29:02,603 - withings - WARNING - (This is one-time activity)
2024-12-01 01:29:02,604 - withings - WARNING -
2024-12-01 01:29:02,604 - withings - INFO - https://account.withings.com/oauth2_user/authorize2?response_type=code&client_id=183e03e1f363110b3551f96765c98c10e8f1aa647a37067a1cb64bbbaf491626&state=OK&scope=user.metrics&redirect_uri=https://jaroslawhartman.github.io/withings-sync/contrib/withings.html&
2024-12-01 01:29:02,604 - withings - INFO -

Token : <PASTE TOKEN>

2024-12-01 01:31:07,832 - withings - INFO - Get Access Token
2024-12-01 01:31:08,313 - withings - INFO - Refresh Access Token
2024-12-01 01:31:08,771 - root - INFO - Fetching measurements from 2024-12-01 00:00 to 2024-12-01 23:59
2024-12-01 01:31:09,406 - withings - INFO - Get Measurements
2024-12-01 01:31:09,856 - root - ERROR - No measurements to upload for date or period specified

You need to visit the URL listed by the script and then - copy Authentification Code back to the prompt.

This is one-time activity and it will not be needed to repeat.

5. running the container:

Subsequent runs will use the saved access tokens in ~/.withings_user.json

$ docker compose run -it --remove-orphans withings-sync                           0.5s
[+] Creating 1/1
✔ Container stack-withings-sync-run-3f24bc7ec7f9  Removed
2024-12-01 01:37:41,500 - withings - INFO - Refresh Access Token
2024-12-01 01:37:41,954 - root - INFO - Fetching measurements from 2024-12-01 00:00 to 2024-12-01 23:59
2024-12-01 01:37:42,563 - withings - INFO - Get Measurements
2024-12-01 01:37:43,069 - root - ERROR - No measurements to upload for date or period specified

6. updating to a newer version:

$ docker compose pull
$ docker compose run -it --remove-orphans withings-sync

This will ensure that both Withings and Garmin session files persist across container restarts in the /config directory.

1.3 Installation of withings-sync with docker compose (using supercronic)

This method leverages the included supercronic package for scheduling jobs directly within the container. This eliminates the need for an external scheduler, allowing the container to manage job execution independently.

Expand to show installation steps.

1. create the following file/directory structure:

Make sure to create the directories (mkdir) & files (touch) upfront or docker will create them as root.

.                                          # STACK_PATH
./.env                                     # .env file containing your variables
./docker-compose.yml                       # docker-compose file
./config/withings-sync/                    # config directory for withings-sync (will be created automatically)
./config/withings-sync/entrypoint.sh       # entrypoint.sh file containing your cmd & arguments

2. contents of an example .env file:

TZ=Europe/Kyiv
STACK_PATH=/home/youruser/withings-sync
GARMIN_USERNAME="your.name@domain.ext"
GARMIN_PASSWORD="YourPasswordHere"

3. contents of an example entrypoint.sh file (updated for --config-folder):

#!/bin/sh
echo "$(( $RANDOM % 59 +0 )) */3 * * * * * poetry run withings-sync --config-folder /config --features BLOOD_PRESSURE" > /home/withings-sync/cronjob
supercronic -debug -passthrough-logs /home/withings-sync/cronjob

4. contents of an example docker-compose.yml file (simplified with --config-folder):

services:
withings-sync:
  image: ghcr.io/jaroslawhartman/withings-sync:latest
  container_name: withings-sync
  stdin_open: true # docker run -i
  tty: true        # docker run -t
  environment:
    - TZ=${TZ:?err}
    - GARMIN_USERNAME=${GARMIN_USERNAME:?err}
    - GARMIN_PASSWORD=${GARMIN_PASSWORD:?err}
  volumes:
    - /etc/localtime:/etc/localtime:ro
    - ${STACK_PATH:?err}/config/withings-sync:/config
    - ${STACK_PATH:?err}/config/withings-sync/entrypoint.sh:/home/withings-sync/entrypoint.sh
  entrypoint: "sh /home/withings-sync/entrypoint.sh"
  restart: unless-stopped

Note: Using --config-folder simplifies the setup by storing both Withings and Garmin session files in the /config directory, eliminating the need for multiple volume mounts.

5. obtaining Withings authorization:

$ docker compose pull
[+] Pulling 13/13
✔ withings-sync Pulled                                                           56.0s
   cb8611c9fe51 Pull complete                                                    4.2s
   52e189a1282f Pull complete                                                    6.4s
   95e68cb0cebc Pull complete                                                   19.0s
   c3ba8bc06a4d Pull complete                                                   19.3s
   fc2b9c85008a Pull complete                                                   21.6s
   0376fca350d9 Pull complete                                                   21.7s
   4f4fb700ef54 Pull complete                                                   21.9s
   c749d618f51d Pull complete                                                   43.0s
   86d00088bd8d Pull complete                                                   43.2s
   98dec7b84387 Pull complete                                                   52.8s
   8825309bd8c2 Pull complete                                                   53.1s
   7747652082d6 Pull complete                                                   53.3s

First start to ensure the container can start successfully:

$ docker compose run -it --remove-orphans --entrypoint "poetry run withings-sync" withings-sync
[+] Creating 1/1
✔ Network stack_default  Created                                                  0.5s
2024-12-01 01:29:02,601 - withings - ERROR - Can\'t read config file /home/youruser/.withings_user.json
2024-12-01 01:29:02,602 - withings - WARNING - User interaction needed to get Authentification Code from Withings!
2024-12-01 01:29:02,603 - withings - WARNING -
2024-12-01 01:29:02,603 - withings - WARNING - Open the following URL in your web browser and copy back the token. You will have *30 seconds* before the token expires. HURRY UP!
2024-12-01 01:29:02,603 - withings - WARNING - (This is one-time activity)
2024-12-01 01:29:02,604 - withings - WARNING -
2024-12-01 01:29:02,604 - withings - INFO - https://account.withings.com/oauth2_user/authorize2?response_type=code&client_id=183e03e1f363110b3551f96765c98c10e8f1aa647a37067a1cb64bbbaf491626&state=OK&scope=user.metrics&redirect_uri=https://jaroslawhartman.github.io/withings-sync/contrib/withings.html&
2024-12-01 01:29:02,604 - withings - INFO -

Token : <PASTE TOKEN>

2024-12-01 01:31:07,832 - withings - INFO - Get Access Token
2024-12-01 01:31:08,313 - withings - INFO - Refresh Access Token
2024-12-01 01:31:08,771 - root - INFO - Fetching measurements from 2024-12-01 00:00 to 2024-12-01 23:59
2024-12-01 01:31:09,406 - withings - INFO - Get Measurements
2024-12-01 01:31:09,856 - root - ERROR - No measurements to upload for date or period specified

6. running the container:

And for subsequent runs we start docker compose and let the container run in the background. Subsequent runs will use the saved access tokens in ~/.withings_user.json

$ docker compose up -d --remove-orphans
[+] Running 1/1
✔ Container withings-sync                         Started                         1.5s

7. logging:

$ docker compose logs withings-sync
withings-sync  | WARN[2024-12-24T09:23:55+01:00] process reaping disabled, not pid 1
withings-sync  | INFO[2024-12-24T09:23:55+01:00] read crontab: /home/withings-sync/cronjob
withings-sync  | DEBU[2024-12-24T09:23:55+01:00] try parse (7 fields): '53 */3 * * * poetry run'
withings-sync  | DEBU[2024-12-24T09:23:55+01:00] failed to parse (7 fields): '53 */3 * * * poetry run': failed: syntax error in day-of-week field: 'poetry'
withings-sync  | DEBU[2024-12-24T09:23:55+01:00] try parse (6 fields): '53 */3 * * * poetry'
withings-sync  | DEBU[2024-12-24T09:23:55+01:00] failed to parse (6 fields): '53 */3 * * * poetry': failed: syntax error in year field: 'poetry'
withings-sync  | DEBU[2024-12-24T09:23:55+01:00] try parse (5 fields): '53 */3 * * *'
withings-sync  | DEBU[2024-12-24T09:23:55+01:00] job will run next at 2024-12-24 09:53:00 +0100 CET  job.command="poetry run withings-sync --features BLOOD_PRESSURE" job.position=0 job.schedule="53 */3 * * *"
withings-sync  | INFO[2024-12-24T09:53:00+01:00] starting                      iteration=0 job.command="poetry run withings-sync --features BLOOD_PRESSURE" job.position=0 job.schedule="53 */3 * * *"
withings-sync  | 2024-12-24 09:53:29,177 - withings - INFO - Refresh Access Token
withings-sync  | 2024-12-24 09:53:29,380 - root - INFO - Fetching measurements from 2024-12-22 18:52 to 2024-12-24 23:59
withings-sync  | 2024-12-24 09:53:29,662 - withings - INFO - Get Measurements
withings-sync  | 2024-12-24 09:53:29,866 - root - INFO - 2024-12-24 08:08:57 This Withings metric contains no weight data or blood pressure.  Not syncing...
withings-sync  | 2024-12-24 09:53:29,868 - root - INFO - 2024-12-24 08:08:57 This Withings metric contains no weight data or blood pressure.  Not syncing...
withings-sync  | 2024-12-24 09:53:29,870 - root - INFO - 2024-12-24 08:08:57 This Withings metric contains no weight data or blood pressure.  Not syncing...
withings-sync  | 2024-12-24 09:53:29,878 - root - INFO - No blood pressure data to sync for FIT file
withings-sync  | 2024-12-24 09:53:29,880 - root - INFO - No TrainerRoad username or a new measurement - skipping sync
withings-sync  | 2024-12-24 09:53:33,665 - root - INFO - Fit file with weight information uploaded to Garmin Connect
withings-sync  | 2024-12-24 09:53:33,666 - withings - INFO - Saving Last Sync
withings-sync  | INFO[2024-12-24T09:53:34+01:00] job succeeded                 iteration=0 job.command="poetry run withings-sync --features BLOOD_PRESSURE" job.position=0 job.schedule="53 */3 * * *"
withings-sync  | DEBU[2024-12-24T09:53:34+01:00] job will run next at 2024-12-24 12:53:00 +0100 CET  job.command="poetry run withings-sync --features BLOOD_PRESSURE" job.position=0 job.schedule="53 */3 * * *"

8. updating to a newer version:

$ docker compose pull
$ docker compose down
$ docker compose up -d --remove-orphans
$ docker image prune -f

This will ensure that both Withings and Garmin session files persist across container restarts in the /config directory.

1.4 Installation of withings-sync with docker (not compose)

This method follows a default approach of utilizing a single container to run one job at a time, then exiting upon completion. It relies on an external scheduler (e.g., cron on the host operating system) to manage job execution.

Expand to show installation steps.
$ docker pull ghcr.io/jaroslawhartman/withings-sync:latest

First start to ensure the script can start successfully (using --config-folder for simplified session management):

Obtaining Withings authorisation:

# Create a local config directory
mkdir -p ./config

# Run with config folder mounted
$ docker run -v ./config:/config --interactive --tty --name withings-sync ghcr.io/jaroslawhartman/withings-sync:latest --config-folder /config --garmin-username=<username> --garmin-password=<password>

Can't read config file /config/.withings_user.json
User interaction needed to get Authentification Code from Withings!

Open the following URL in your web browser and copy back the token. You will have *30 seconds* before the token expires. HURRY UP!
(This is one-time activity)

https://account.withings.com/oauth2_user/authorize2?response_type=code&client_id=183e03e1f363110b3551f96765c98c10e8f1aa647a37067a1cb64bbbaf491626&state=OK&scope=user.metrics&redirect_uri=https://jaroslawhartman.github.io/withings-sync/contrib/withings.html&

Token : <token>
Withings: Get Access Token
Withings: Refresh Access Token
Withings: Get Measurements
   Measurements received
JaHa.WAW.PL
Garmin Connect User Name: JaHa.WAW.PL
Fit file uploaded to Garmin Connect

And for subsequent runs (both session files persist in ./config):

$ docker start -i withings-sync
Withings: Refresh Access Token
Withings: Get Measurements
   Measurements received
JaHa.WAW.PL
Garmin Connect User Name: JaHa.WAW.PL
Fit file uploaded to Garmin Connect

Note: Using --config-folder /config stores both Withings and Garmin session files in the mounted directory, making them persist across container restarts.

2. Usage Instructions

usage: withings-sync [-h] [--version] [--garmin-username GARMIN_USERNAME] [--garmin-password GARMIN_PASSWORD] [--trainerroad-username TRAINERROAD_USERNAME] [--trainerroad-password TRAINERROAD_PASSWORD] [--fromdate DATE] [--todate DATE] [--to-fit] [--to-json]
                     [--output BASENAME] [--no-upload] [--features BLOOD_PRESSURE [BLOOD_PRESSURE ...]] [--verbose | --silent] [--dump-raw] [--config-folder PATH]

A tool for synchronisation of Withings (ex. Nokia Health Body) to Garmin Connect and Trainer Road or to provide a json string.

options:
  -h, --help            show this help message and exit
  --version, -V         show program's version number and exit
  --garmin-username GARMIN_USERNAME, --gu GARMIN_USERNAME
                        Username to log in to Garmin Connect.
  --garmin-password GARMIN_PASSWORD, --gp GARMIN_PASSWORD
                        Password to log in to Garmin Connect.
  --trainerroad-username TRAINERROAD_USERNAME, --tu TRAINERROAD_USERNAME
                        Username to log in to TrainerRoad.
  --trainerroad-password TRAINERROAD_PASSWORD, --tp TRAINERROAD_PASSWORD
                        Password to log in to TrainerRoad.
  --fromdate DATE, -f DATE
                        Date to start syncing from. Ex: 2023-12-20
  --todate DATE, -t DATE
                        Date for the last sync. Ex: 2023-12-30
  --to-fit, -F          Write output file in FIT format.
  --to-json, -J         Write output file in JSON format.
  --output BASENAME, -o BASENAME
                        Write downloaded measurements to file.
  --no-upload           Won't upload to Garmin Connect or TrainerRoad.
  --features BLOOD_PRESSURE [BLOOD_PRESSURE ...]
                        Enable Features like BLOOD_PRESSURE.
  --verbose, -v         Run verbosely.
  --silent, -s          Run silently (suppress INFO messages).
  --dump-raw, -R        Dump the raw Withings API JSON for the selected date range to file. If --output is provided, the file will be BASENAME.withings_raw.json. Otherwise, a default filename with the date range will be used.
  --config-folder PATH, -c PATH
                        Path to config folder for session files (if not specified, uses legacy paths in home directory).

3. Providing credentials

3.1 Providing credentials via environment variables

You can use the following environment variables for providing the Garmin and/or Trainerroad credentials:

  • GARMIN_USERNAME
  • GARMIN_PASSWORD
  • TRAINERROAD_USERNAME
  • TRAINERROAD_PASSWORD

The CLI also uses python-dotenv to populate the variables above. Therefore setting the environment variables has the same effect as placing the variables in a .env file in the working directory.

3.2 Providing credentials via secrets files

You can also populate the following 'secrets' files to provide the Garmin and/or Trainerroad credentials:

  • /run/secrets/garmin_username
  • /run/secrets/garmin_password
  • /run/secrets/trainerroad_username
  • /run/secrets/trainerroad_password

Secrets are useful in an orchestrated container context — see the Docker Swarm or Rancher docs for more information on how to securely inject secrets into a container.

3.3 Order of priority for credentials

In the case of credentials being available via multiple means (e.g. environment variables and secrets files), the order of resolution for determining which credentials to use is as follows, with later methods overriding credentials supplied by an earlier method:

  1. Read secrets file(s)
  2. Read environment variable(s), variables set explicitly take precedence over values from a .env file.
  3. Use command invocation argument(s)

4. Tips

4.1 Garmin SSO errors

Some users reported errors raised by the Garmin SSO login:

withings_sync.garmin.APIException: SSO error 401

or

withings_sync.garmin.APIException: SSO error 403

These errors are raised if a user tries to login too frequently. E.g. by running the script every 10 minutes.

We recommend to run the script around 8-10 times per day (every 2-3 hours).

See also: https://github.com/jaroslawhartman/withings-sync/issues/31

4.2 Garmin auth

You can configure the location of the garmin session file with the variabe GARMIN_SESSION.

Note: If you specify both --config-folder and the GARMIN_SESSION environment variable, the --config-folder option takes precedence.

4.3 Config folder

By default, withings-sync stores session files in your home directory:

  • ~/.withings_user.json for Withings authentication
  • ~/.garmin_session for Garmin authentication

You can use the --config-folder or -c argument to store all session files in a custom folder:

# Store session files in ~/.withings-sync
withings-sync --config-folder ~/.withings-sync

# Store session files in a custom location
withings-sync --config-folder /path/to/my/config

When using a custom config folder:

  • The folder will be created automatically if it doesn't exist
  • If you have existing session files in the legacy locations, the script will show you where to copy them from
  • This is useful for Docker installations or for keeping your home directory clean

4.4 Run a periodic Kubernetes job

  1. Create the secret using kubectl in the command line.
export GARMIN_USERNAME="user@username.com"
export GARMIN_PASSWORD="superPassword"
export TRAINERROAD_USERNAME: "name@name.com"
export TRAINERROAD_PASSWORD: "superPassword"
kubectl create secret generic withings-secret --from-literal=GARMIN_USERNAME=$GARMIN_USERNAME --from-literal=GARMIN_PASSWORD=$GARMIN_PASSWORD
--from-literal=TRAINERROAD_USERNAME=$TRAINERROAD_USERNAME --from-literal=TRAINERROAD_PASSWORD=$TRAINERROAD_PASSWORD
  1. Create the PVC.
kubectl apply -f k8s-pvc.yaml
  1. Run the bootstrap pod, which attaches to the PVC for storing credentials.
kubectl apply -f k8s-bootstrap.yaml

The bootstrap pod stays on indefinitely to allow time for you to exec in, and generate the credentials.

  1. Exec into the bootstrap pod, generate credentials and store them in the PVC.
kubectl exec -it bootstrap-withings-sync -- sh

From within the bootstrap pod:

# Create a config directory in the PVC
mkdir -p /data/config

# Run with config folder to store all session files
poetry run withings-sync --config-folder /data/config --fromdate=<RECORDED_DATE>

It is important that this run includes a date that has a record, as a record is required for the program to attempt an upload to garmin in order to create the session files for garmin. The command above will allow entering the withings token and the MFA code for garmin. After successful auth, the credentials will be automatically stored in /data/config/:

  • /data/config/.withings_user.json
  • /data/config/.garmin_session
  1. Create the cron job. Update the cron job command to use the config folder:
kubectl apply -f k8s-job.yaml

Note: Update your k8s-job.yaml to include --config-folder /data/config in the container command.

5 For advanced users - registering own Withings application

Instead of using the provided Withings application tokens you can register your own app with Withings and use that one instead.

If you are not sure you need this, you most likely won't.

The script has been registered as a Withings application and got assigned Client ID and Consumer Secret. If you wish to create your own application - feel free!

Note, registering it is quite cumbersome, as you need to have a callback URL and an Icon. Anyway, when done, you should have the following identifiers:

Identfier Example
Client ID 183e03.................765c98c10e8f1aa647a37067a1......baf491626
Consumer Secret a75d65.................4c16719ef7bd69fa7c5d3fd0ea......ed48f1765
Callback URI https://jhartman.pl/withings/notify

Configure them in config/withings_app.json, for example:

{
    "callback_url": "https://wieloryb.uk.to/withings/withings.html",
    "client_id": "183e0******0b3551f96765c98c1******b64bbbaf491626",
    "consumer_secret": "a75d65******1df1514c16719ef7bd69fa7*****2e2b0ed48f1765"
}

For the callback URL you will need to setup a webserver hosting contrib/withings.html.

To do this in a Docker installation, you can use the environment variable WITHINGS_APP to point to a mounted withings_app.json

Example docker-compose:

  withings-sync:
    container_name: withings-sync
    image: ghcr.io/jaroslawhartman/withings-sync:latest
    volumes:
      - "withings-sync:/root"
      - "/etc/localtime:/etc/localtime:ro"
    environment:
      WITHINGS_APP: /home/withings-sync/withings_app.json
(...)

You can then add the app-config in withings-sync/withings_app.json

6. Release

Release works via the GitHub Draft a new Release function. The version key in pyproject.toml will be bumped automatically (Version will be written to pyproject.toml file).

Docker Image

Container images are created automagically by GitHub Action and published to ghcr.

Pypi & GitHub

Will be conducted automatically within the Github-Release cycle. This needs the permission on the pypi-project. The python packages are added to the GitHub releases by a GitHub Action.

7. References

8. Credits / Authors

  • Based on withings-garmin by Masayuki Hamasaki, improved to support SSO authorization in Garmin Connect 2.
  • Based on withings-garmin-v2 by Jarek Hartman, improved Python 3 compatability, code-style and setuptools packaging, Kubernetes and Docker support.

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

withings_sync-5.3.0.tar.gz (31.4 kB view details)

Uploaded Source

Built Distribution

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

withings_sync-5.3.0-py3-none-any.whl (27.5 kB view details)

Uploaded Python 3

File details

Details for the file withings_sync-5.3.0.tar.gz.

File metadata

  • Download URL: withings_sync-5.3.0.tar.gz
  • Upload date:
  • Size: 31.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for withings_sync-5.3.0.tar.gz
Algorithm Hash digest
SHA256 dbfffeab28db9736b2be058ff80856d5893b02d08d8bfe79d1e88b79f10d7299
MD5 8f8c7034301129231c4f2b328f926eb7
BLAKE2b-256 cd47ea15fb698c2b177d3018ced4620bc1d407eceaf5657b2a8d6da8e14e1b0e

See more details on using hashes here.

File details

Details for the file withings_sync-5.3.0-py3-none-any.whl.

File metadata

  • Download URL: withings_sync-5.3.0-py3-none-any.whl
  • Upload date:
  • Size: 27.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for withings_sync-5.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 482f55f9fe0bd3acdff854cf971a1c76e4b8c91f70df90354970fc0d25879bdd
MD5 b6bff6a3a32e7e0ee7e7f4b06c9589e9
BLAKE2b-256 b0731eaea45ebb65b1bd0fdc61834879e8d190d22ac240881c64cc68ed479fa7

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