Garmin

v0.1.0

Integrate with Garmin Connect to fetch and analyze deep fitness metrics including sleep, body battery, resting heart rate, stress, and training status. Use t...

0· 474·0 current·0 all-time
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Suspicious
medium confidence
Purpose & Capability
The code and SKILL.md match the stated purpose: they use the garminconnect library and 1Password to fetch Garmin credentials and produce fitness metrics. However the skill metadata claims no required environment variables or primary credential even though the scripts clearly require OP_SERVICE_ACCOUNT_TOKEN and a 1Password item (GARMIN_1P_ITEM_NAME / GARMIN_1P_VAULT). That mismatch between declared requirements and actual runtime needs is an incoherence.
!
Instruction Scope
Runtime scripts read 1Password via the op CLI (subprocess calls), attempt to read ~/.config/op/service-account-token, set GARMIN_EMAIL/PASSWORD into environment for Python, log in to Garmin, dump session files into /tmp/garmin-session/, and cache JSON under /root/clawd/data/fitness/garmin/. The morning summary script also invokes another skill's script (/root/clawd/skills/strava/scripts/training-summary.sh). These actions go beyond simple API calls and include cross-skill invocation, local file writes, and reading local 1Password service-account tokens — all of which should be explicit in the skill metadata/instructions.
Install Mechanism
There is no formal install spec (instruction-only), which is low-risk in principle. The SKILL.md recommends pip install garminconnect and creating a venv; scripts assume a venv at ./venv and a Python 3.12 site-packages path. The pip recommendation includes --break-system-packages (an unusual flag) which may be inappropriate on some systems. No remote downloads or archive extractions are present in the skill files.
!
Credentials
The skill requires access to a 1Password service account token (OP_SERVICE_ACCOUNT_TOKEN) and a 1Password Login item containing the Garmin email/password, which is proportional to logging into Garmin but was not declared in the registry metadata. The skill also reads ~/.config/op/service-account-token if OP_SERVICE_ACCOUNT_TOKEN is not set. Asking for service-account-level 1Password access is powerful — verify the token's scope and consider using a least-privilege credential. No other unrelated credentials are requested.
Persistence & Privilege
The skill writes session tokens to /tmp/garmin-session/ and caches daily JSON in /root/clawd/data/fitness/garmin/. It does not set always:true or modify other skills' configuration, but it does call another skill's script by absolute path. Persistent files and cross-skill invocation increase blast radius and should be reviewed, but they are consistent with caching and generating summaries.
What to consider before installing
Things to check before installing: - Metadata mismatch: the registry says "no required env vars" but the scripts require OP_SERVICE_ACCOUNT_TOKEN and (optionally) GARMIN_1P_ITEM_NAME / GARMIN_1P_VAULT. Expect to provide a 1Password service account token and a Login item for your Garmin account. - 1Password token scope: the skill uses the op CLI with a service account token. Ensure that token is least-privilege and that you trust the environment where it will run — a service token can read items in the vault it has access to. - Inspect and trust garminconnect: the Python library performs login and session dumping (client.garth.dump) — review what session files are created in /tmp/garmin-session/ and decide whether temporary caching is acceptable. - File writes and cross-skill calls: the skill caches data under /root/clawd/data/fitness/garmin/ and invokes /root/clawd/skills/strava/scripts/training-summary.sh. Confirm those paths and ownerships are acceptable and that calling another skill's script is intended. - Virtualenv & install: create and use an isolated virtualenv as recommended (avoid pip with --break-system-packages unless you understand its effect on your OS). Verify Python version/path assumptions (the script inserts a python3.12 site-packages path). If you want to proceed: create a dedicated 1Password Login item for Garmin, limit the service-account token's vault access, run the skill in an isolated environment, and manually review the garminconnect library and the session/cache directories after first run. If you want metadata fixed: ask the publisher to declare OP_SERVICE_ACCOUNT_TOKEN and GARMIN_1P_* in requires.env and to document the exact files and paths the skill will write/read.

Like a lobster shell, security has layers — review code before you run it.

fitnessvk9737qk44bg2dea2gfp2mpbsks81fw6egarminvk9737qk44bg2dea2gfp2mpbsks81fw6ehealthvk9737qk44bg2dea2gfp2mpbsks81fw6elatestvk9737qk44bg2dea2gfp2mpbsks81fw6e
474downloads
0stars
1versions
Updated 1mo ago
v0.1.0
MIT-0

Garmin Connect Integration Skill

Deep fitness metrics from Garmin Connect for enhanced training insights and recovery-aware nudges.

Features

  • Training Status: Recovery time, training load, VO2 max
  • Sleep Analysis: Duration, quality, sleep stages
  • Body Battery: Energy levels throughout day
  • Daily Readiness: Is Brian recovered enough to train hard?
  • Heart Rate: Resting HR trends, stress levels
  • Activity Details: More detailed metrics than Strava

Why Garmin + Strava?

Strava: Social, activities, segments, ride tracking
Garmin: Physiological metrics, recovery, sleep, training load

Combined = Smart nudges that respect recovery status!

Setup

1. Install Dependencies

pip3 install garminconnect --break-system-packages
# Or using a virtual environment (recommended):
# python3 -m venv ./venv
# source ./venv/bin/activate
# pip install garminconnect

2. Store Credentials in 1Password

Create a new "Login" item in your 1Password vault (e.g., "Personal") with the following details:

  • Title: Garmin Connect (or a custom name you prefer)
  • Username: Your Garmin Connect email address
  • Password: Your Garmin Connect password

If you use a custom title or a different vault, set the GARMIN_1P_ITEM_NAME and GARMIN_1P_VAULT environment variables before running the scripts. Example:

export GARMIN_1P_ITEM_NAME="My Garmin Login"
export GARMIN_1P_VAULT="MyFamilyVault"

Ensure your OP_SERVICE_ACCOUNT_TOKEN is set up for 1Password CLI authentication:

export OP_SERVICE_ACCOUNT_TOKEN=$(cat ~/.config/op/service-account-token)

3. Test Connection

./scripts/garmin-login.sh

Usage

Get Today's Stats

./scripts/get-stats.sh

Returns:

  • Body battery (current/forecast)
  • Sleep last night
  • Training status
  • Recovery time remaining
  • Resting heart rate

Get Sleep Data

./scripts/get-sleep.sh [days_back]

Returns sleep duration, quality, stages for last N days.

Check Recovery Status

./scripts/check-recovery.sh

Returns whether Brian is recovered enough for hard training.

Integration with Strava Nudges

Enhanced decision logic:

Before nudging for a hard workout:

  1. Check Garmin recovery time
  2. Check body battery level
  3. Check sleep quality last night
  4. Adjust intensity recommendation

Example:

  • Strava says: "Thursday tempo ride"
  • Garmin says: "Recovery time: 24h, body battery: 45%"
  • Nudge becomes: "Thursday ride scheduled, but recovery still needed. Easy Zone 2 instead of tempo today?"

Data Structure

Stats Object

{
  "body_battery": {
    "current": 75,
    "charged": true,
    "forecast": 85
  },
  "sleep": {
    "duration_hours": 7.2,
    "quality": "good",
    "deep_sleep_hours": 1.8,
    "rem_hours": 1.5
  },
  "training_status": {
    "status": "productive",
    "vo2_max": 52,
    "recovery_time_hours": 12
  },
  "heart_rate": {
    "resting": 48,
    "current": 62,
    "stress_level": 25
  }
}

Smart Nudge Enhancement Examples

Scenario 1: Poor Sleep + Hard Workout Day

Without Garmin: "Thursday tempo ride time!"
With Garmin: "You only got 5 hours sleep last night. Maybe take today easy? Light Zone 2 or rest."

Scenario 2: Recovered + Good Conditions

Without Garmin: "Tuesday ride day"
With Garmin: "Fully recovered (body battery 85%, 8h sleep) + perfect weather. Great day for that tempo ride! 🚴"

Scenario 3: High Stress Day

Without Garmin: "Evening gym time!"
With Garmin: "Stress level high today (68). Maybe skip gym and prioritize recovery?"

Morning Briefing Enhancement

Current:

🚴 Fitness Update:
Last ride: 2 days ago
This week: 3 rides, 87km

With Garmin:

🚴 Fitness Update:
**Sleep:** 7.5h (good quality, 2h deep)
**Recovery:** ✅ Fully recovered
**Body Battery:** 82% (charged overnight)
**Resting HR:** 48 bpm (normal)

Last ride: 2 days ago
This week: 3 rides, 87km
**Training Status:** Productive (VO2 max: 52)

Configuration

Edit config.json (create if it doesn't exist):

{
  "recovery_thresholds": {
    "body_battery_low": 40,
    "body_battery_good": 70,
    "min_sleep_hours": 6.5,
    "max_recovery_time_hours": 12
  },
  "nudge_modifications": {
    "respect_recovery": true,
    "downgrade_intensity_if_tired": true,
    "skip_gym_if_high_stress": true
  }
}

Note: This config.json should be created in the skill's root directory (/root/clawd/skills/garmin/).

API Reference

Using garminconnect Python library:

  • get_stats() - Daily stats summary
  • get_sleep_data() - Sleep metrics
  • get_body_battery() - Energy levels
  • get_training_status() - Training load, recovery
  • get_heart_rates() - HR data

Rate limits: No official limit, but be reasonable (cache data, don't spam).

Dependencies

  • Python 3.7+
  • garminconnect library
  • 1Password CLI (op)
  • jq for JSON parsing (if needed by other scripts)

Privacy

  • ✅ Credentials stored in 1Password
  • ✅ Session tokens cached temporarily in /tmp/garmin-session/
  • ✅ Data queried on-demand, not stored long-term by the skill (though the system might cache in /root/clawd/data/fitness/garmin/ as per TOOLS.md)
  • ✅ No external sharing
  • ✅ Read-only access to Garmin

Future Enhancements

  • Correlate sleep quality → work productivity
  • Predict when Brian will be recovered
  • Compare son's Garmin data (if he has one)
  • Long-term trends (fitness improving?)

Comments

Loading comments...