Files
private_health/bridge
Matthias Jacob 2370254e23 bridge: add steps and pulse via Health's Daily Values / Measurements APIs
Health v3 splits writable metrics across three distinct endpoint families
instead of one unified /entries resource (per nextcloud/health's
src/metrics.ts and src/api/*.ts, which are more current than its Markdown
docs): journal entries, one-off measurements, and per-day upserted daily
values.

- weight and steps are Daily Values (PUT /daily-values/{metricKey}/{date});
  steps are sourced from Companion's daily_totals aggregate (already
  deduplicated across sources) instead of summing raw per-window records.
- pulse (heart rate) is a Measurement (POST /measurements); handles both
  raw records and bucketed resolution windows, using the window average
  and a deterministic id for the latter since they carry no HC uuid.
- Both endpoints require an explicit unit (weight: kg, steps: steps,
  pulse: bpm) and Measurements only accept context manual/checkin/checkout,
  not an automation-specific value -- both previously caused 400s.
- sleep_duration is dropped: despite being documented, it isn't an actual
  Daily Value, Measurement, or journal metric in the shipped app (absent
  from GET /configuration entirely).
- Logging now reports per-metric success/failure counts and uses Health's
  metric-key vocabulary consistently instead of mixing in Companion's raw
  payload field names.
2026-09-17 03:48:47 +02:00
..

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.

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.

Scope

  • 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.
  • 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 README's architecture diagram).
  • Blood pressure, steps, activity (add once this MVP is proven out).
  • Containerized deployment (currently a local script only, per the current iteration's scope).