github the-momentum/open-wearables 0.7.0
0.7.0 - Google Health API & SensorBio

6 hours ago

Highlights

We encourage you to use our official Docker images:

Google Health API & SensorBio

In our latest release, we’re introducing two new integrations with providers. Both integrations will be further developed, but it’s important to note two things:

  • The Google Health API replaces the Fitbit API, which is soon to be deprecated by Google
  • SenorBio does not currently support webhooks
image

Admin UI Dashboard improvements

On less efficient infrastructure, the front-end dashboard could take up to several seconds to load data. We optimised this by making improvements to React and removing unnecessary information. In return, we added more details about the volume of stored data and its specific types (see screenshot below). We also improved the display of large numbers (previously, even millions were shown in thousands). The dashboard should now load instantly, even with very large volumes of data and limited RAM.

image

More information on data sources for the frontend

On the frontend, each piece of data (workouts, sleep sessions, menstrual cycles, activity) will display more information. Until now, we’ve only shown the provider from which the data was saved. Now, the original provider will also be displayed (if applicable – for example, Apple allows data to be synchronised from devices belonging to other providers) along with the device from which the data was retrieved (currently, in the example described earlier, for Apple this will simply be an iPhone, but we also intend to start recording information about the original device soon, provided the providers allow it).

You’ll probably often notice gaps in this information; we intend to rectify them in the next release, as this requires changes to the way data is fetched from the sources.

image image image image

The option to delete data associated with a selected provider

From now on, the user profile includes an option to delete data from a selected provider. This also automatically disconnects the connection.

image image

SDK applications management in Settings

From now on, you can manage SDK applications via the frontend settings (Admin UI).

image

Logging improvements

The first change is an improvement to the SDK’s data-saving process. Previously, a single incorrect row would result in the loss of the entire batch. Now, instead, we display the sync status for the problematic rows and log their exact errors to Sentry.

image image

In addition, we have added a lot of new information to the logs, which may be useful for debugging.
HTTP logs from app:

  • response_body (an error details)
  • request_bytes (how many bytes we are expecting from the payload)
  • error_cause_type (can be helpful to decide whether an error parsing the body response_body is caused by connection or decode error)
  • error_cause_msg
  • content_type (should be application/json)
  • content_encoding (for example gzip)
{"timestamp": "2026-07-30T17:41:05.671050+00:00", "level": "error", "message": "http_request", "provider": null, "method": "POST", "path": "/api/v1/sdk/users/00000000-0000-0000-0000-0000000000bb/sync", "status": 400, "duration_ms": 17.7, "request_bytes": 3, "response_body": "There was an error parsing the body", "content_type": "application/json", "error_cause_type": "UnicodeDecodeError", "error_cause_msg": "'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte"}
{"timestamp": "2026-07-30T17:41:05.681616+00:00", "level": "error", "message": "http_request", "provider": null, "method": "POST", "path": "/api/v1/sdk/users/00000000-0000-0000-0000-0000000000bb/sync", "status": 400, "duration_ms": 0.6, "request_bytes": 62, "response_body": "There was an error parsing the body", "content_type": "application/json", "content_encoding": "gzip", "error_cause_type": "UnicodeDecodeError", "error_cause_msg": "'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte"}
{"timestamp": "2026-07-30T17:41:05.732281+00:00", "level": "error", "message": "http_request", "provider": null, "method": "POST", "path": "/api/v1/sdk/users/00000000-0000-0000-0000-0000000000bb/sync", "status": 400, "duration_ms": 0.9, "request_bytes": 1000000, "response_body": "There was an error parsing the body", "content_type": "application/json", "error_cause_type": "ClientDisconnect", "error_cause_msg": ""}

Logs from celery worker:

  • first_error_loc (position of the first broken row in S3 payload)
  • data_type (SDK related, example HKQuantityTypeIdentifierWalkingStepLength)
  • success (SDK related, example true)
  • record_count (SDK related, int)
  • task_type (SDK related, example foreground)
  • low_power (SDK related, example false)
  • thermal_state (SDK related, example nominal)
  • types (sync status related, example ["basal_energy", "energy", "heart_rate", "steps"])
{"timestamp": "2026-07-30T15:14:21.143378+00:00", "level": "info", "message": "Apple data import completed", "provider": "apple", "action": "apple_sdk_import_complete", "batch_id": "89d47924-a674-48bd-9001-32c3ec3065e2", "user_id": "3c213ef4-38f3-4c0c-89d3-c782a741125d", "incoming_records": 1750, "incoming_workouts": 125, "incoming_sleep": 125, "records_saved": 2023, "workouts_saved": 124, "sleep_saved": 124, "validation_ms": 56.5}
{"timestamp": "2026-07-30T15:14:21.143502+00:00", "level": "warning", "message": "Apple SDK dropped invalid records", "provider": "apple", "action": "apple_sdk_records_dropped", "batch_id": "89d47924-a674-48bd-9001-32c3ec3065e2", "user_id": "3c213ef4-38f3-4c0c-89d3-c782a741125d", "dropped_count": 3, "first_error_loc": "records[0].value", "first_error_msg": "Input should be a valid decimal"}
{"timestamp": "2026-08-11T11:20:50.019232+00:00", "level": "info", "message": "SDK log events received",
 "provider": "apple", "action": "sdk_logs_received", "batch_id": "823f86a1-b76a-4f48-....",
 "user_id": "d771d1c3-3098-....", "event_count": 2,
 "event_types": ["historical_data_type_sync_end", "device_state"], "sdk_version": "0.13.0",
 "data_type": "HKQuantityTypeIdentifierWalkingStepLength", "success": true, "record_count": 966,
 "task_type": "foreground", "low_power": false, "thermal_state": "nominal"}
{"timestamp": "2026-08-11T11:21:33.975802+00:00", "level": "info", "message": "Sync status",
 "provider": "apple", "action": "sync_status", "status": "success", "source": "sdk",
 "stage": "completed", "run_id": "a899503a-e7bb-....", "user_id": "d771d1c3-...",
 "items_processed": 1439, "types": ["basal_energy", "energy", "heart_rate", "steps"],
 "detail": "Apple batch saved"}

What's Changed

Google Health API

SensorBio

Oura

  • chore(backend): Use Oura 30-sec sleep stage granularity over 5-min by @czajkub in #1327

Data model

Frontend improvements

Observability & deployments

  • chore(backend): Move SDK payload validation to celery worker by @czajkub in #1323
  • fix(backend): resolve $defs refs in SDK sync OpenAPI schema by @bartmichalak in #1335
  • chore: SDK logs validation, optional response logging on 4xx by @czajkub in #1344
  • feat(backend): add traceback for HTTP 500 by @KaliszS in #1351
  • feat(backend): drop & log incorrect rows from SDK sync by @KaliszS in #1354
  • feat(backend): celery worker socket_keepalive by @KaliszS in #1357
  • feat(backend): add request size to the logs by @KaliszS in #1371
  • feat(backend): adding root cause of error to the logs by @KaliszS in #1372
  • fix(backend): accept SDK log timeRange without startDate by @bartmichalak in #1373
  • fix(frontend): dev image allows changing VITE_API_URL by @KaliszS in #1384
  • fix: return number of successes, correct filter for hydration log by time in GH API by @czajkub in #1405

Other

Docs & chores

New Contributors

Full Changelog: 0.6.3...0.7.0

Don't miss a new open-wearables release

NewReleases is sending notifications on new releases.