Skip to main content

vital-sync

Personal health data aggregation and analytics toolkit.

Integrates Oura Ring, Hevy workout app, Renpho scale, and MyNetDiary into a local cache with statistical analysis. Works standalone via CLI or as a context provider for LLM-based health assistants (Hermes, ChatGPT, Claude).

PyPI version Python 3.11+ License: MIT

Features

  • Oura Ring v2 API client — sleep periods, daily scores (sleep/readiness/activity), HRV, heart rate, SpO₂
  • Hevy workout API client — workout sync, exercise tracking, 1RM estimation, muscle group analysis
  • Statistics engine — rolling baselines, hybrid deviation detection (adaptive z-scores + absolute floors), Pearson correlations, circadian consistency
  • LLM context builders — pre-processed JSON output for morning check-ins and weekly trend reports
  • CSV import — Renpho body composition and MyNetDiary nutrition data (manual: export CSV from each app, then vital-sync-import)
  • Zero external dependencies — pure Python stdlib

Quick Start

pip install vital-sync

Set your API keys as environment variables:

export OURA_API_KEY="your-oura-personal-access-token"
export HEVY_API_KEY="your-hevy-api-key"  # requires Hevy Pro subscription

Or create a .env file in your working directory with the same variables.

Fetch your sleep data

from vital_sync.oura_client import pull_all

records = pull_all(days=7)
for date, data in sorted(records.items()):
    print(f"{date}: {data.get('sleep_duration_hours', '?'):.1f}h, score={data.get('sleep_score')}")

Analyse workout trends

from vital_sync.hevy_client import sync_workouts, workout_summary

workouts = sync_workouts()
summary = workout_summary(workouts, days=14)
print(f"Sessions: {summary['workouts']}, Volume: {summary['total_volume_kg']}kg")

Generate a morning check-in JSON

vital-sync-morning

Outputs a structured JSON object with today's sleep data, 7-day trends, recent workouts, and gap detection — ready for an LLM to interpret.

Generate a weekly health report JSON

vital-sync-weekly

Outputs comprehensive analysis: baselines vs previous week, correlations (e.g. training volume → sleep quality), circadian consistency, and detected deviations.

Architecture

vital_sync/
├── oura_client.py      # Oura Ring API v2
├── hevy_client.py      # Hevy workout API
├── analytics.py        # Statistics engine
├── morning_checkin.py  # Daily context builder
├── weekly_report.py    # Weekly trend report
├── pre_pull_check.py   # Backup + gap detection
├── import_csv.py       # Renpho/MyNetDiary CSV import
└── sleep_tags.py       # Nightly factor tracking

Data is stored as a local JSON cache at ~/.vital_sync/cache.json (configurable via VITAL_SYNC_CACHE env var).

Optional personal configuration is read from ~/.vital_sync/config.json (configurable via VITAL_SYNC_CONFIG). Copy config.example.json to that path and adjust it:

{
  "sleep_tags": {
    "early_wake": {
      "enabled": true,
      "before": "07:00"
    }
  }
}

Without this config, the automatic early wake sleep tag is disabled. Set enabled to false to turn it off again, or change before to whatever local wake-time cutoff you want.

Hermes Agent Integration

vital-sync is the health data backend for Hermes Agent but works with any LLM pipeline. The morning_checkin and weekly_report modules output structured JSON — Hermes cron jobs run them and inject the output as LLM context.

Setup

pip install vital-sync
export OURA_API_KEY="..." HEVY_API_KEY="..."
# Initialise the cache with a full pull
python -m vital_sync.pre_pull_check

Morning check-in (daily at 11:00)

Creates a Hermes cron job that runs vital-sync-morning, producing JSON with last night's sleep, 7-day trend, workout recaps, and gap detection. Hermes injects this into your morning health prompt:

# The script output (JSON) becomes LLM context for the morning check-in prompt
hermes cronjob create \
  --schedule "0 11 * * *" \
  --name "morning-health-checkin" \
  --script "$(which vital-sync-morning)" \
  --no-agent  # raw script output delivered as context

Weekly report (Sunday at 20:00)

hermes cronjob create \
  --schedule "0 20 * * 0" \
  --name "weekly-health-report" \
  --script "$(which vital-sync-weekly)" \
  --no-agent

Daily data pull (10:00)

Runs the backup + gap check, pulls fresh Oura data, syncs Hevy workouts, and merges into cache:

# Create a wrapper script that runs daily pull + merge
cat > ~/.vital_sync/pull.sh << 'EOF'
#!/bin/bash
source ~/.hermes/.env
set -e
python -m vital_sync.pre_pull_check
python -c "
from vital_sync.oura_client import pull_all
from vital_sync.hevy_client import sync_workouts
from vital_sync.analytics import load_cache, save_cache, merge_hevy_into_records
import os
cache = os.environ.get('VITAL_SYNC_CACHE', os.path.expanduser('~/.vital_sync/cache.json'))
records = load_cache(cache)
new_data = pull_all(days=2)
# Merge Oura into records
from datetime import date
from vital_sync.analytics import DailyRecord
for day_str, data in sorted(new_data.items()):
    r = DailyRecord(date=date.fromisoformat(day_str))
    for k, v in data.items():
        if k != 'date' and v is not None:
            setattr(r, k, v)
    replaced = False
    for i, existing in enumerate(records):
        if existing.date.isoformat() == day_str:
            r.sources = list(set(getattr(existing, 'sources', []) + ['oura']))
            for field in ['mood','notes','sleep_tags','calories_in','protein_g','body_fat_pct','weight_kg','hevy_workouts','hevy_total_volume_kg']:
                if getattr(existing, field, None) is not None:
                    setattr(r, field, getattr(existing, field))
            records[i] = r
            replaced = True
            break
    if not replaced:
        records.append(r)
workouts = sync_workouts()
records = merge_hevy_into_records(workouts, records)
records.sort(key=lambda r: r.date)
save_cache(records, cache)
print(f'Cache updated: {len(records)} records')
"
EOF
chmod +x ~/.vital_sync/pull.sh

hermes cronjob create \
  --schedule "0 10 * * *" \
  --name "health-data-pull" \
  --script ~/.vital_sync/pull.sh \
  --no-agent

Environment variables

Variable Default Description
OURA_API_KEY Oura Personal Access Token
HEVY_API_KEY Hevy API key
VITAL_SYNC_CACHE ~/.vital_sync/cache.json Cache file path
VITAL_SYNC_DATA_DIR ~/.vital_sync/ Data directory
VITAL_SYNC_SLEEP_TAGS ~/.vital_sync/sleep_tags.json Sleep tags file
VITAL_SYNC_CONFIG ~/.vital_sync/config.json Personal config file

Cache Schema

{
  "date": "2026-04-27",
  "sleep_score": 79.0,
  "readiness_score": 80.0,
  "sleep_duration_hours": 7.41,
  "sleep_efficiency": 88.0,
  "deep_sleep_min": 123.0,
  "rem_sleep_min": 82.5,
  "avg_hr": 60.12,
  "lowest_hr": 54.0,
  "hrv_ms": 32.0,
  "steps": 8500,
  "bedtime": "23:42:29",
  "wake_time": "08:06:17",
  "hevy_workouts": 1,
  "hevy_total_volume_kg": 4520.0,
  "hevy_muscle_groups": ["chest", "triceps", "shoulders"],
  "sources": ["oura", "hevy"]
}

Development

git clone https://github.com/mishmishb/vital-sync.git
cd vital-sync
pip install -e ".[dev]"
pytest

License

MIT — see LICENSE.

Author

Built by mishmishb as part of a personal health analytics pipeline. Contributions welcome.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vital_sync-0.1.4.tar.gz (124.7 kB view details)

Uploaded Source

Built Distribution

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

vital_sync-0.1.4-py3-none-any.whl (46.0 kB view details)

Uploaded Python 3

File details

Details for the file vital_sync-0.1.4.tar.gz.

File metadata

  • Download URL: vital_sync-0.1.4.tar.gz
  • Upload date:
  • Size: 124.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vital_sync-0.1.4.tar.gz
Algorithm Hash digest
SHA256 2e3d0481b2ede2d91a83f026a2ba9585c2ab6cec68f736c62dd4ecced6e3000a
MD5 548a635fefd1bd88e61aae1651fba968
BLAKE2b-256 6b54745fa15dd42efa6f357373bccc696127abe3a6d847bc8962296c7a5abff4

See more details on using hashes here.

Provenance

The following attestation bundles were made for vital_sync-0.1.4.tar.gz:

Publisher: release.yml on mishmishb/vital-sync

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

File details

Details for the file vital_sync-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: vital_sync-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 46.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vital_sync-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 cb89c3c14269ae61843d7da3df4421143e5aefce29e36f8e4698128daa49c098
MD5 98d7c24039f5cbf9222a65b8cafba5f3
BLAKE2b-256 0fbe1005e384ed8cbe532c403c29af58f2544a043b0b4aec2a4030af78757227

See more details on using hashes here.

Provenance

The following attestation bundles were made for vital_sync-0.1.4-py3-none-any.whl:

Publisher: release.yml on mishmishb/vital-sync

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

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 files

0.1.3

2 files

0.1.2

2 files

0.1.0

2 files

Supported by

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