Package: withings-sync
withings-syncA tool for synchronisation of the Withings API to:Garmin ConnectTrainer Roadraw JSON outputInstallation$pipinstallwithings-syncUsageusage: withings-sync [-h] [--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] [--verbose]

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

optional arguments:
  -h, --help            show this help message 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
  --todate DATE, -t DATE
  --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.
  --features            Enable Features
                        BLOOD_PRESSURE = sync blood pressure
  --no-upload           Won't upload to Garmin Connect or TrainerRoad.
  --verbose, -v         Run verboselyProviding credentials via environment variablesYou can use the following environment variables for providing the Garmin and/or Trainerroad credentials:GARMIN_USERNAMEGARMIN_PASSWORDTRAINERROAD_USERNAMETRAINERROAD_PASSWORDThe 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.envfile in the working directory.Providing credentials via secrets filesYou 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_passwordSecrets are useful in an orchestrated container context — see theDocker SwarmorRancherdocs for more information on how to securely inject secrets into a container.Order of priority for credentialsIn the case of credentials being available via multiple means (e.g.environment variablesandsecrets files), the order of resolution for determining which credentials to use is as follows, with later methods overriding credentials supplied by an earlier method:Read secrets file(s)Read environment variable(s), variables set explicitly take precedence over values from a.envfile.Use command invocation argument(s)Obtaining Withings Authorization CodeWhen running for a very first time, you need to obtain Withings authorization:$withings-sync-f2019-01-25-v
Can'treadconfigfileconfig/withings_user.json
UserinteractionneededtogetAuthentificationCodefromWithings!

OpenthefollowingURLinyourwebbrowserandcopybackthetoken.Youwillhave*30seconds*beforethetokenexpires.HURRYUP!(Thisisone-timeactivity)https://account.withings.com/oauth2_user/authorize2?response_type=code&client_id=183e03e1f363110b3551f96765c98c10e8f1aa647a37067a1cb64bbbaf491626&state=OK&scope=user.metrics&redirect_uri=https://wieloryb.uk.to/withings/withings.html&Token: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.TipsGarmin SSO errorsSome users reported errors raised by the Garmin SSO login:withings_sync.garmin.APIException: SSO error 401orwithings_sync.garmin.APIException: SSO error 403These 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/31Docker$ docker pull ghcr.io/jaroslawhartman/withings-sync:masterFirst start to ensure the script can start successfully:Obtaining Withings authorisation:$ docker run -v $HOME:/root --interactive --tty --name withings ghcr.io/jaroslawhartman/withings-sync:master --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://wieloryb.uk.to/withings/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 ConnectAnd for subsequent runs:$ docker start -i withings
Withings: Refresh Access Token
Withings: Get Measurements
   Measurements received
JaHa.WAW.PL
Garmin Connect User Name: JaHa.WAW.PL
Fit file uploaded to Garmin ConnectGarmin authYou can configure the location of the garmin session file with the variabeGARMIN_SESSION.Run a periodic Kubernetes jobEdit the credentials incontrib/k8s-job.yamland run:$kubectlapply-fcontrib/k8s-job.yamlFor advanced users - registering own Withings applicationThe script has been registered as a Withings application and got assignedClient IDandConsumer Secret. If you wish to create your own application - feel free!First you need a Withings account.Sign up here.Then you need a Withings developer app registered.Create your app here.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:IdentfierExampleClient ID183e03.................765c98c10e8f1aa647a37067a1......baf491626Consumer Secreta75d65.................4c16719ef7bd69fa7c5d3fd0ea......ed48f1765Callback URIhttps://jhartman.pl/withings/notifyConfigure them inconfig/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 hostingcontrib/withings.html.To do this in a Docker installation, you can use the environment variableWITHINGS_APPto point to a mountedwithings_app.jsonExample 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: /root/withings_app.json
(...)You can then add the app-config inwithings-sync/withings_app.jsonRun a periodic docker-compose cronjobWe take the official docker image and override the entrypoint to crond.If you have completed the initial setup (withings_user.json created and working), you can create the following configversion: "3.8"
services:
  withings-sync:
    container_name: withings-sync
    image: ghcr.io/jaroslawhartman/withings-sync:master
    volumes:
      - "${VOLUME_PATH}/withings-sync:/root" 
      - /etc/localtime:/etc/localtime:ro
    environment:
      - TZ=${TIME_ZONE}
    entrypoint: "/root/entrypoint.sh"Theentrypoint.shwill then register the cronjob. For example:#!/bin/sh
echo "$(( $RANDOM % 59 +0 )) */3 * * * withings-sync --gu garmin-username --gp 'mypassword' -v | tee -a /root/withings-sync.log" > /etc/crontabs/root
crond -f -l 6 -L /dev/stdoutThis will run the job every 3 hours (at a random minute) and writing the output to console and the/root/withings-sync.log.ReleaseRelease works via the GitHubDraft a new Releasefunction.
Keep in mind to bump theversionkey insetup.py.Docker ImageAn image is created magically by GitHub Action and published
toghcr.pypiYou'll find a script to create and upload a release to pypi herecontrib/do_release.sh.
It requirestwine.
This needs the permission on thepypi-project.ReferencesSSO authorization derived fromhttps://github.com/cpfair/tapiriikTrainerRoad API fromhttps://github.com/stuwilkins/python-trainerroadCredits / AuthorsBased onwithings-garminby Masayuki Hamasaki, improved to support SSO authorization in Garmin Connect 2.Based onwithings-garmin-v2by Jarek Hartman, improved Python 3 compatability, code-style and setuptools packaging, Kubernetes and Docker support.