# Fitbit Sense 2 + Hevy Companion Spike

Date: 2026-05-26
Owner: Chicho / Pipo

## Verdict

**PARTIAL but worth building.**

A private Fitbit Sense 2 app/clockface that shows Hevy-derived workout plan, tracks sets/reps/weights/RIR, runs a rest timer, vibrates, and syncs to a backend is feasible.

The hard boundary: **Hevy does not expose live active workout state**. The Fitbit app cannot mirror the live Hevy mobile session. The workable design is: Fitbit becomes the live workout companion, then backend creates/updates the completed Hevy workout through the Hevy API.

## Device / Fitbit OS feasibility

Sense 2 / Versa 4 third-party development is unofficial/unsupported, but working paths exist:

- SDK stable: `@fitbit/sdk@6.1.0`.
- Pre-release SDK: `@fitbit/sdk@7.2.0-pre.0`, includes `rhea` / `hera` targets.
  - `rhea` = Sense 2.
  - `hera` = Versa 4.
- Fitbit Studio is shut down; development is CLI-based.
- No official Sense 2 simulator; develop against Sense/Versa 3 first, then build for `rhea`.
- Public Gallery publishing is effectively dead/unreliable, but private installs/sideload are usable for personal devices.
- Argentina is not affected by EEA third-party Fitbit restrictions.

## Install paths

### Path A — Private Gallery link
Best for clockfaces, simpler distribution.

1. Create Fitbit developer account / Gallery App Manager project.
2. Build `.fba` with `rhea` target.
3. Upload privately to GAM.
4. Install from private link in Fitbit app.

Risk: GAM is flaky and approvals/public publishing are dead-ish.

### Path B — Developer Bridge / USB sideload
Best for full apps and iteration.

Known repo: `cmengler/fitbit-app-versa4`.

Rough Linux flow:

```bash
sudo apt-get install libsecret-1-dev
export FITBIT_QA_COMMANDS=1
export FITBIT_DEVBRIDGE_DUMP=1   # optional debug
npx fitbit
# fitbit$ hosts
# fitbit$ connect phone
# fitbit$ connect device
# fitbit$ build-and-install
```

Device setup:
- Sense 2 on charger.
- Watch Settings → Developer Bridge → USB debugging ON.
- Fitbit phone app → Device → Developer Menu → Developer Bridge ON.

Linux may need udev rule for Fitbit USB vendor/product from Fitbit developer bridge docs.

## Fitbit app architecture useful for Hevy companion

A Fitbit OS app can include:

- Device app UI: screens/buttons/touch/scroll.
- Companion app running in Fitbit phone app.
- Messaging between watch and companion.
- Companion `fetch()` to HTTPS endpoints.
- Local storage/settings.
- Timers and vibration.

For our use case:

- Watch UI shows current exercise, set, target reps, target weight, target RIR.
- Buttons: previous/next exercise, set done, +/- reps, +/- weight, RIR selector, start/rest skip.
- Timer: rest countdown + vibration.
- Companion/backend syncs state.

## Hevy API findings

Official docs: https://api.hevyapp.com/docs/

Useful endpoints:

- `GET /v1/routines`
- `GET /v1/routines/{id}`
- `GET /v1/workouts`
- `GET /v1/workouts/{id}`
- `POST /v1/workouts`
- `PUT /v1/workouts/{id}`
- `GET /v1/workouts/events`
- `GET /v1/exercise_templates`
- `GET /v1/exercise_history/{templateId}`

Important limitations:

- No live active workout endpoint.
- No incremental set append endpoint.
- Webhooks are create-only / not enough for live state.
- Completed workout creation is supported and rich enough: exercises, sets, `weight_kg`, `reps`, set type, `rpe`.
- RIR can be mapped to RPE (`RPE = 10 - RIR`) where needed.
- Hevy API is young and can change; use our existing wrapper/skill rules.

## Recommended MVP architecture

```text
Hevy routines/API
      ↓
Pipo backend: /api/workout-plan/today
      ↓ HTTPS
Fitbit companion app
      ↓ messages
Sense 2 app UI
      ↓ set events
Fitbit companion → Pipo backend /api/live-workout/events
      ↓
At finish: backend POST /v1/workouts to Hevy
```

Backend should be source of truth during workout:

- active workout session
- current exercise index
- completed sets
- rest timer state
- substitutions
- notes/RIR
- final Hevy payload

Watch should be offline-tolerant enough to keep local state if companion/backend is flaky.

## YouTube Music observation

Chicho pointed out that the official YouTube Music Fitbit app can show the currently playing track and send controls from the watch back to the phone app. This confirms the core interaction model is valid on Sense 2: watch UI ↔ phone-side companion/service ↔ remote/app state.

The distinction for Hevy is not Fitbit capability; it is Hevy exposure. YouTube Music has an official Google/Fitbit integration path and can control playback state. Hevy does not expose a public live-workout session API, so our app either:

1. becomes the live companion itself and writes the completed Hevy workout at the end; or
2. reverse-engineers Hevy mobile/web live/session internals if they exist and are reachable.

This shifts the next research/build risk from “can Fitbit do this?” to “can we either own the live state cleanly or discover Hevy live-state hooks?”.

## MVP scope

1. Import current Hevy routine into local backend.
2. Start workout session from backend or watch.
3. Watch displays one exercise at a time:
   - name
   - target sets
   - target weight
   - target reps/range
   - target RIR/RPE
4. Log set:
   - reps
   - weight
   - RIR
   - optional note/substitution
5. Rest timer with vibration.
6. Finish workout → backend creates completed workout in Hevy.
7. Pipo reads Hevy log and adjusts next session.

## Spike tasks next

### 001 — Dev bridge smoke test

Given the Sense 2 and Pixel/Fitbit app are available, when we enable Developer Bridge and run the Fitbit CLI from Linux, then `hosts`, `connect phone`, and `connect device` should work.

Verdict gate: if this fails, use private Gallery or abandon Sense 2 native app.

### 002 — Minimal Sense 2 app

Given dev bridge works, when we sideload a hello-world app for `rhea`, then it should launch on Sense 2 and receive a message from companion.

### 003 — Companion HTTPS fetch

Given the app is installed, when companion calls Pipo backend HTTPS endpoint, then watch displays returned workout exercise.

### 004 — Hevy completed workout creation

Given a mock Fitbit workout session, when backend builds a Hevy payload, then `POST /v1/workouts` creates the completed workout correctly.

## Key sources

- Fitbit SDK npm: https://www.npmjs.com/package/@fitbit/sdk
- Fitbit SDK toolchain: https://github.com/Fitbit/fitbit-sdk-toolchain
- Developer bridge: https://github.com/Fitbit/developer-bridge
- cmengler sideload approach: https://github.com/cmengler/fitbit-app-versa4
- build targets drop-in: https://github.com/cmengler/fitbit-sdk-build-targets
- Sense2/Versa4 guide: https://github.com/yeohongred/fitbit-versa4-sense2-sdk
- Hevy API docs: https://api.hevyapp.com/docs/
