bridge: update README for the Daily Values / Measurements architecture

Replace the outdated /entries-only description with the three-endpoint
model, document daily_totals-based steps sourcing, and move sleep_duration
to "not yet covered" with an explanation of why (not implemented in
Health v3 at all, not just disabled for this user).
This commit is contained in:
Matthias Jacob
2026-09-17 03:49:04 +02:00
parent 2370254e23
commit 9865b43945
+46 -9
View File
@@ -1,8 +1,16 @@
# Bridge: Life Dashboard Companion → Nextcloud Health v3
Minimal MVP. Forwards only the `weight` records from the Companion webhook
payload to Health v3's `POST /entries` endpoint, one entry per record, since
weight is currently the only OpenScale-relevant metric Health v3 has built in.
Forwards the Health Connect types that are both sent by the Companion app and
supported by Health v3, split across Health's three write APIs:
- **Daily Values** (`PUT /daily-values/{metricKey}/{date}`, one upsert per
calendar day): `weight`, `steps`.
- **Measurements** (`POST /measurements`, one call per timestamped reading):
`pulse` (heart rate).
Which metrics a given Health instance actually knows is detected at startup
via `GET /configuration`, since shipped releases can lag behind the
documented API (see "Not yet covered" below).
Requires Python 3.10+ (uses `str | None` type hints).
@@ -20,21 +28,50 @@ uv run server.py
Point the Companion app's webhook URL at `http://<this-host>:8080/webhook/health-connect`.
Set `NC_TIMEOUT_SECONDS` (default `30`) if your Nextcloud instance responds slowly.
## Scope
- On startup, fetches `GET /configuration` and only forwards metrics the
Health instance actually knows; unknown metrics are logged and skipped
instead of failing every request with an error. Metrics it knows but has
disabled for the user are enabled automatically via `PUT /configuration`.
- Verifies `X-Signature: sha256=<hmac>` on every request; rejects with 401 on
mismatch (Companion does not retry on 401).
- Only reads the `weight` array; everything else in the payload is ignored.
- Uses the Health Connect record `uuid` as Health's `operationId` for
idempotent retries where it parses as a UUID.
- Reads `weight`, `daily_totals` (for steps), and `heart_rate` from the
payload; everything else is ignored.
- Steps use Companion's `daily_totals`, which Health Connect already
deduplicates across sources (phone, watch, ...) into one total per day;
summing the raw per-window `steps` records ourselves would double-count,
so those are ignored in favor of the daily total.
- Weight uses the UTC calendar date of each reading as the Daily Value's
date key (Health doesn't expose the owning user's timezone to this
bridge), so a reading taken right around local midnight could land on the
"wrong" day.
- Heart rate: handles both raw records (`bpm`/`time`) and bucketed
resolution windows (`bucket_start`/`avg`); bucketed windows get a
deterministic id derived from the window since they carry no HC uuid.
- Daily Value writes are idempotent by construction (one value per
metric/date, last write wins); Measurement writes use the Health Connect
record `uuid` (or a derived id for bucketed windows) as Health's
`operationId` for idempotent retries.
- Returns 502 on partial forwarding failure so the Companion app retries the
whole payload later (transient failures are retried by the app).
## Not yet covered
- Body fat / muscle mass / body water (not yet built into Health v3; muscle
mass and body water aren't even on Health v3's roadmap — see the main
- Sleep duration: despite being documented in Health v3's data model, it
isn't an actual Daily Value, Measurement, or journal metric in the shipped
app yet (absent from `GET /configuration`'s metric list entirely), so
there's currently no endpoint to send it to.
- Blood pressure, body fat, blood glucose, oxygen saturation, and
temperature: the Companion app doesn't send these Health Connect types at
all yet.
- Muscle mass and body water: not on Health v3's roadmap at all (see the main
README's architecture diagram).
- Blood pressure, steps, activity (add once this MVP is proven out).
- Hydration and exercise/movement: both apps have these concepts, but with
incompatible shapes (a volume/duration in Companion vs. a closed set of
event options in Health), so a faithful mapping isn't possible without
lossy guessing.
- Containerized deployment (currently a local script only, per the current
iteration's scope).