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).
3.6 KiB
3.6 KiB
Bridge: Life Dashboard Companion → Nextcloud Health v3
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).
Setup
# One-time: obtain a Nextcloud app password via Login Flow v2.
# Opens a browser tab, confirm the login there, then this writes credentials.json.
uv run nc_login_flow.py https://your-nextcloud.example.com
# Set the same HMAC secret configured under "Webhook Headers" in the Companion app.
export NC_WEBHOOK_SECRET="..."
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 /configurationand 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 viaPUT /configuration. - Verifies
X-Signature: sha256=<hmac>on every request; rejects with 401 on mismatch (Companion does not retry on 401). - Reads
weight,daily_totals(for steps), andheart_ratefrom 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-windowstepsrecords 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'soperationIdfor 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
- 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).
- 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).