Metadata-Version: 2.5
Name: withings-to-intervals-icu
Version: 0.1.0
Summary: Copy Withings weight and body fat measurements into Intervals.icu wellness.
Project-URL: Homepage, https://github.com/bartTC/withings-to-intervals-icu
Project-URL: Issues, https://github.com/bartTC/withings-to-intervals-icu/issues
Author: Martin Mahner
License-Expression: MIT
License-File: LICENSE
Keywords: body fat,intervals.icu,weight,wellness,withings
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: python-dotenv>=1.0
Requires-Dist: tqdm>=4.66
Requires-Dist: tzdata; sys_platform == 'win32'
Description-Content-Type: text/markdown

# withings-to-intervals-icu

Copy your Withings weight and body fat measurements into [Intervals.icu](https://intervals.icu) wellness.

- Fills only empty fields by default. A day that already has a weight in Intervals.icu keeps it, but still gets body fat if that's missing. Use `--overwrite` to replace existing values.
- Takes one reading per day: the earliest weigh-in, which is usually the morning one.
- Skips Withings readings the scale couldn't confidently assign to you (for example on a shared scale), and skips days you've locked in Intervals.icu.

```sh
uvx withings-to-intervals-icu                 # last 30 days
uvx withings-to-intervals-icu --all           # full history
uvx withings-to-intervals-icu --start 2024-01-01 --end 2024-12-31
uvx withings-to-intervals-icu --all --dry-run # preview, write nothing
uvx withings-to-intervals-icu --all --overwrite
```

## Setup

### 1. Intervals.icu API key

In Intervals.icu go to **Settings → Developer Settings** and generate an API key.

### 2. Withings developer app

Withings only offers OAuth2, so you need your own (free) developer app:

1. Sign in at the [Withings Developer Dashboard](https://developer.withings.com/dashboard/) and create a **Public API** application.
2. Set the **Callback URL** to `https://example.com`, exactly as written, with no path. Withings rejects `localhost` and IP addresses and only accepts a public `https://` URL that answers a `HEAD` request with `200`. The example.com homepage passes that check, and the page doesn't need to do anything because you'll copy the code out of the browser's address bar. The code is useless without your client secret and expires after 30 seconds.

   If Withings won't accept example.com, use any page you control instead and pass it with `--withings-redirect-uri`.
3. Note the **Client ID** and **Client Secret**.

### 3. Credentials

Pass each value as a command-line option, set the matching environment variable, or put it in a `.env` file in the directory you run the command from. Options win over environment variables, which win over `.env`.

| Option | Environment variable | Required | |
|---|---|---|---|
| `--withings-client-id` | `WITHINGS_CLIENT_ID` | yes | From the Withings app |
| `--withings-client-secret` | `WITHINGS_CLIENT_SECRET` | yes | From the Withings app |
| `--withings-redirect-uri` | `WITHINGS_REDIRECT_URI` | no | Defaults to `https://example.com`; must match the registered callback URL exactly |
| `--intervals-icu-api-key` | `INTERVALS_ICU_API_KEY` | yes | From Intervals.icu settings |
| `--intervals-icu-athlete-id` | `INTERVALS_ICU_ATHLETE_ID` | no | Defaults to `0`, meaning the owner of the API key |

```sh
uvx withings-to-intervals-icu --all \
  --withings-client-id ... --withings-client-secret ... \
  --intervals-icu-api-key ...
```

Secrets passed as options end up in your shell history and are visible in `ps`, so prefer environment variables for scheduled runs.

### 4. First run

The first run opens a browser so you can approve access. After you approve, Withings redirects you to your callback URL with a `?code=...` in the address bar. Copy the whole URL and paste it into the terminal within 30 seconds, because that's how long Withings codes stay valid.

The resulting token is saved to `~/.config/withings-to-intervals-icu/withings-token.json` and refreshed automatically, so later runs (including cron) don't need a browser. If the token ever stops working, run again with `--reauth`.

## Development

```sh
uv run pytest
uv run withings-to-intervals-icu --help
```

## Releasing

Pushing a version tag publishes to PyPI from GitHub Actions using [Trusted Publishing](https://docs.pypi.org/trusted-publishers/), so no API token is stored anywhere. The workflow runs the tests first and refuses to publish if the tag doesn't match `version` in `pyproject.toml`.

```sh
# bump version in pyproject.toml, commit, then:
git tag v0.1.0 && git push --tags
```

One-time setup before the first release: on PyPI, add a pending publisher (Account → Publishing) for project `withings-to-intervals-icu`, owner `bartTC`, repository `withings-to-intervals-icu`, workflow `publish.yml`, environment `pypi`.
