All-in-one local development tool for cloud wearable integrations - CLI + FastAPI server + ngrok integration for WHOOP, Garmin, and Fitbit
Project description
Synheart Wear CLI (wear)
All-in-one local development tool for cloud wearable integrations โ Complete CLI + FastAPI server + ngrok integration for WHOOP, Garmin, and Fitbit testing.
๐ What is This?
The Synheart Wear CLI combines a command-line interface with an embedded FastAPI server for local wearable development:
CLI Features:
- ๐ง Manage OAuth tokens (list, refresh, revoke)
- ๐ฅ Pull data from cloud APIs
- ๐ Inspect webhook events
- ๐ Start/stop local dev server
Server Features:
- ๐ OAuth flows for cloud wearables (WHOOP, Fitbit, Garmin*)
- ๐ช Webhook endpoints for real-time data
- ๐ Automatic ngrok tunnel exposure
- ๐พ Local token storage (dev mode) or DynamoDB + KMS (production)
- ๐ Data normalization to Synheart format
Perfect for:
- Testing SDK integrations locally
- Developing apps with WHOOP/Garmin data
- Prototyping cloud wearable features
- Full OAuth flow testing
* Garmin support in development
๐ Prerequisites
- Python 3.11+
- ngrok account (free): https://ngrok.com/
- Wearable API credentials (WHOOP, Garmin, etc.)
- AWS Account (optional for production token storage)
๐ Quick Start
1. Install
Option A: Install from PyPI (Recommended)
# Install the CLI globally
pip install synheart-wear-cli
# Verify installation
wear version
wear --help
Option B: Install from Source
# Clone the repository
git clone https://github.com/synheart-ai/synheart-wear-cli.git
cd synheart-wear-cli
# Install in development mode
pip install -e ".[dev]"
# Verify installation
python3 wear.py --help
# Or if installed globally:
wear --help
Note: All required libraries are automatically installed. No separate cloning needed.
2. Configure ngrok
# Get your auth token from https://dashboard.ngrok.com/get-started/your-authtoken
ngrok config add-authtoken YOUR_TOKEN
3. Create Environment File
Create .env.local in the CLI directory:
# WHOOP Credentials
WHOOP_CLIENT_ID=your_whoop_client_id
WHOOP_CLIENT_SECRET=your_whoop_client_secret
# AWS (optional - for production token storage)
AWS_REGION=us-east-1
DYNAMODB_TABLE=synheart-wear-tokens
KMS_KEY_ID=alias/synheart-wear
# Development Mode (automatically enabled by CLI)
DEV_MODE=true
WEBHOOK_RECORD=true
4. Start Development Server
# Start WHOOP connector with ngrok
python3 wear.py start dev --vendor whoop --port 8000
# Or with auto-open browser for OAuth:
python3 wear.py start dev --vendor whoop --open-browser
# The CLI will automatically:
# โ
Start local FastAPI server
# โ
Start ngrok tunnel
# โ
Display ngrok URL for SDK configuration
# โ
Enable webhook recording
# โ
Setup hot-reload for code changes
Output:
๐ Starting Synheart Wear
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ Configuration:
Mode: dev
Vendor: whoop
Port: 8000
Auto-reload: โ
enabled
Webhook record: โ
enabled
๐ Auto-loaded environment from: .env.local
๐ Endpoints:
API Docs: http://localhost:8000/docs
Health Check: http://localhost:8000/health
OAuth Auth: http://localhost:8000/v1/oauth/authorize
Webhooks: http://localhost:8000/v1/webhooks/whoop
๐ Starting ngrok tunnel...
โ
ngrok tunnel started: https://abc123-xyz.ngrok-free.app
๐ฑ SDK Configuration:
Use this URL in your Flutter app:
baseUrl: 'https://abc123-xyz.ngrok-free.app'
๐ Commands
wear start dev - Start Local Development Server
Start local server with automatic ngrok tunneling.
# Start WHOOP connector
python3 wear.py start dev --vendor whoop --port 8000
# Start with auto-open browser for OAuth
python3 wear.py start dev --vendor whoop --open-browser
# Start unified service (all vendors - Garmin coming soon)
python3 wear.py start dev --port 8000
# Use specific environment file
python3 wear.py start dev --vendor whoop --env .env.production
# Disable auto-reload
python3 wear.py start dev --vendor whoop --no-reload
# Verbose logging
python3 wear.py start dev --vendor whoop --verbose
Options:
--vendor, -v- Vendor to run (whoop,garmin, or omit for all)--port, -p- Port to run on (default: 8000)--reload/--no-reload- Auto-reload on code changes (default: enabled)--env- Environment file to load (.env.local,.env.production)--open-browser- Automatically open OAuth authorization URL--webhook-record/--no-webhook-record- Enable webhook recording (default: enabled)--verbose- Enable verbose logging
What it does:
- โ Starts FastAPI server on specified port
- โ Automatically starts ngrok tunnel
- โ Displays ngrok URL for SDK configuration
- โ Enables auto-reload for code changes
- โ
Records webhooks to
__dev__/webhooks_recent.jsonl
wear pull once - Fetch Data from Cloud API
Pull data from vendor cloud API (requires OAuth connection first).
# Pull WHOOP recovery data (last 7 days)
python3 wear.py pull once --vendor whoop --since 7d
# Pull specific data types
python3 wear.py pull once --vendor whoop --since 30d --data-types recovery,sleep,workouts
# Pull from specific user
python3 wear.py pull once --vendor whoop --user-id abc123 --since 14d
Options:
--vendor- Vendor to pull from (required)--since- Time range (e.g.,7d,30d,2h)--data-types- Comma-separated data types (recovery, sleep, workouts, cycles)--user-id- Specific user ID (optional)
wear tokens - Manage OAuth Tokens
List, refresh, or revoke OAuth tokens.
# List all tokens
python3 wear.py tokens list
# List tokens for specific vendor
python3 wear.py tokens list --vendor whoop
# Refresh expired token
python3 wear.py tokens refresh --vendor whoop --user-id abc123
# Revoke token (disconnect user)
python3 wear.py tokens revoke --vendor whoop --user-id abc123
wear webhook - Webhook Management
Inspect webhook events recorded during development.
# Inspect recent webhooks (last 50)
python3 wear.py webhook inspect --limit 50
# Filter by vendor
python3 wear.py webhook inspect --vendor whoop --limit 100
# Show webhook details
python3 wear.py webhook inspect --verbose
๐๏ธ Architecture
synheart-wear-cli/
โโโ wear.py # Main CLI entry point
โโโ server/ # Local development server
โ โโโ whoop_api.py # WHOOP OAuth + data endpoints
โ โโโ garmin_api.py # Garmin OAuth + data endpoints
โ โโโ unified_api.py # Unified service (all vendors)
โ โโโ whoop_connector.py # WHOOP connector logic
โโโ libs/
โ โโโ py-cloud-connector/ # OAuth token management
โ โโโ py-normalize/ # Data normalization
โโโ __dev__/ # Development data (auto-generated)
โโโ webhooks_recent.jsonl
โโโ tokens.json
๐ง Development Workflow
1. Start Local Server
python3 wear.py start dev --vendor whoop --open-browser
2. Complete OAuth Flow
The browser will open automatically. Log in and authorize.
3. Configure SDK
Use the ngrok URL displayed in the terminal in your app:
Flutter:
final whoopProvider = WhoopProvider(
baseUrl: 'https://abc123-xyz.ngrok-free.app',
redirectUri: 'synheart://oauth/callback',
);
Swift:
let whoopProvider = WhoopProvider(
baseUrl: URL(string: "https://abc123-xyz.ngrok-free.app")!,
redirectUri: "synheart://oauth/callback"
)
Kotlin:
val whoopProvider = WhoopProvider(
baseUrl = "https://abc123-xyz.ngrok-free.app",
redirectUri = "synheart://oauth/callback"
)
4. Fetch Data
Once connected, fetch data from your app or use the CLI:
python3 wear.py pull once --vendor whoop --since 7d
5. Test Webhooks
Webhooks are automatically recorded to __dev__/webhooks_recent.jsonl:
python3 wear.py webhook inspect --limit 10
๐ก API Endpoints
The local server exposes these endpoints:
WHOOP
GET /v1/oauth/authorize- Get OAuth authorization URLGET /v1/oauth/callback- OAuth callback (GET)POST /v1/oauth/callback- OAuth callback (POST, mobile)POST /v1/webhooks/whoop- Webhook handlerDELETE /v1/oauth/disconnect- Disconnect userGET /v1/data/{user_id}/recovery- Fetch recovery dataGET /v1/data/{user_id}/sleep- Fetch sleep dataGET /v1/data/{user_id}/workouts- Fetch workout dataGET /v1/data/{user_id}/cycles- Fetch cycle data
Unified Service
GET /v1/whoop-cloud/oauth/authorize- WHOOP OAuthGET /v1/garmin-cloud/oauth/authorize- Garmin OAuthPOST /v1/whoop-cloud/webhooks/whoop- WHOOP webhooksPOST /v1/garmin-cloud/webhooks/garmin- Garmin webhooks
Health Check
GET /health- Service health status
API Docs: http://localhost:8000/docs
๐ Security
- Token Storage: DynamoDB with KMS encryption (production)
- Dev Mode: Tokens stored locally in
__dev__/tokens.json(development) - Webhook Verification: HMAC signature validation
- Environment Variables: Never commit
.env.localfiles
๐งช Testing
# Run tests
pytest
# Run specific test
pytest tests/test_oauth.py -v
# Run with coverage
pytest --cov=server --cov-report=html
๐ Troubleshooting
ngrok Issues
Problem: "ngrok endpoint already online"
Solution:
# Kill all ngrok processes
pkill -f ngrok
# Or restart with different port
python3 wear.py start dev --port 8001
Port Already in Use
Problem: "Port 8000 is already in use"
Solution:
# Find process using port
lsof -i :8000
# Kill process
kill $(lsof -ti :8000)
# Or use different port
python3 wear.py start dev --port 8001
OAuth Flow Fails
Problem: "Authentication failed"
Solution:
- Check environment variables in
.env.local - Verify redirect URI matches vendor configuration
- Check ngrok URL is correct
- Look at server logs for detailed errors
๐ Internal Libraries
py-cloud-connector
OAuth token management for wearable vendors with DynamoDB + KMS encryption.
Features:
VendorType: Enum for supported vendors (whoop, garmin, fitbit)TokenStore: DynamoDB-based token storage with encryptionTokenSet: Standardized OAuth token data structure
See libs/py-cloud-connector/README.md
py-normalize
Data normalization utilities for converting vendor-specific formats to Synheart format.
Features:
DataNormalizer: Converts vendor data to common formatDataType: Enum for data types (recovery, sleep, workout, etc.)NormalizedData: Common data structure for all vendors
See libs/py-normalize/README.md
๐ Links
- Main Repository: synheart-wear
- Flutter SDK: synheart-wear-dart
- Android SDK: synheart-wear-kotlin
- iOS SDK: synheart-wear-swift
- ngrok: https://ngrok.com/
- Issues: GitHub Issues
๐ License
MIT License - see LICENSE file
Made with โค๏ธ by the Synheart AI Team
Technology with a heartbeat.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file synheart_wear_cli-0.1.1.tar.gz.
File metadata
- Download URL: synheart_wear_cli-0.1.1.tar.gz
- Upload date:
- Size: 69.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8b61bf5b3732dbb48093caa84b2ed37c14cc823e80b1a76c757594d395196fd
|
|
| MD5 |
0407700df5d601d783df8de9386f60f3
|
|
| BLAKE2b-256 |
7aa860170254fa02344dcc0a64e3b7e21948d3cf7a7f20ffb4efe05a2f00c0d2
|
Provenance
The following attestation bundles were made for synheart_wear_cli-0.1.1.tar.gz:
Publisher:
publish.yml on synheart-ai/synheart-wear-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synheart_wear_cli-0.1.1.tar.gz -
Subject digest:
a8b61bf5b3732dbb48093caa84b2ed37c14cc823e80b1a76c757594d395196fd - Sigstore transparency entry: 737264999
- Sigstore integration time:
-
Permalink:
synheart-ai/synheart-wear-cli@28dfe3c2fd8e945e6ed824e7f375cea68ac398df -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/synheart-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@28dfe3c2fd8e945e6ed824e7f375cea68ac398df -
Trigger Event:
release
-
Statement type:
File details
Details for the file synheart_wear_cli-0.1.1-py3-none-any.whl.
File metadata
- Download URL: synheart_wear_cli-0.1.1-py3-none-any.whl
- Upload date:
- Size: 62.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
033712aa5b4bcca6bd6845198e3db4651f0d1a81de7dae2e7c9b7a8fee609209
|
|
| MD5 |
e06269e0c97c8bba4d2aea665c62d55f
|
|
| BLAKE2b-256 |
2846d59e2a70bc5a53b241484c7e9716676733d61eefabfc497a9403fafca765
|
Provenance
The following attestation bundles were made for synheart_wear_cli-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on synheart-ai/synheart-wear-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synheart_wear_cli-0.1.1-py3-none-any.whl -
Subject digest:
033712aa5b4bcca6bd6845198e3db4651f0d1a81de7dae2e7c9b7a8fee609209 - Sigstore transparency entry: 737265000
- Sigstore integration time:
-
Permalink:
synheart-ai/synheart-wear-cli@28dfe3c2fd8e945e6ed824e7f375cea68ac398df -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/synheart-ai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@28dfe3c2fd8e945e6ed824e7f375cea68ac398df -
Trigger Event:
release
-
Statement type: