Skip to main content

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)

PyPI version Python Versions License: MIT Downloads

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 URL
  • GET /v1/oauth/callback - OAuth callback (GET)
  • POST /v1/oauth/callback - OAuth callback (POST, mobile)
  • POST /v1/webhooks/whoop - Webhook handler
  • DELETE /v1/oauth/disconnect - Disconnect user
  • GET /v1/data/{user_id}/recovery - Fetch recovery data
  • GET /v1/data/{user_id}/sleep - Fetch sleep data
  • GET /v1/data/{user_id}/workouts - Fetch workout data
  • GET /v1/data/{user_id}/cycles - Fetch cycle data

Unified Service

  • GET /v1/whoop-cloud/oauth/authorize - WHOOP OAuth
  • GET /v1/garmin-cloud/oauth/authorize - Garmin OAuth
  • POST /v1/whoop-cloud/webhooks/whoop - WHOOP webhooks
  • POST /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.local files

๐Ÿงช 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:

  1. Check environment variables in .env.local
  2. Verify redirect URI matches vendor configuration
  3. Check ngrok URL is correct
  4. 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 encryption
  • TokenSet: 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 format
  • DataType: Enum for data types (recovery, sleep, workout, etc.)
  • NormalizedData: Common data structure for all vendors

See libs/py-normalize/README.md

๐Ÿ”— Links

๐Ÿ“„ 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

synheart_wear_cli-0.1.1.tar.gz (69.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

synheart_wear_cli-0.1.1-py3-none-any.whl (62.5 kB view details)

Uploaded Python 3

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

Hashes for synheart_wear_cli-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a8b61bf5b3732dbb48093caa84b2ed37c14cc823e80b1a76c757594d395196fd
MD5 0407700df5d601d783df8de9386f60f3
BLAKE2b-256 7aa860170254fa02344dcc0a64e3b7e21948d3cf7a7f20ffb4efe05a2f00c0d2

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file synheart_wear_cli-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for synheart_wear_cli-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 033712aa5b4bcca6bd6845198e3db4651f0d1a81de7dae2e7c9b7a8fee609209
MD5 e06269e0c97c8bba4d2aea665c62d55f
BLAKE2b-256 2846d59e2a70bc5a53b241484c7e9716676733d61eefabfc497a9403fafca765

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page