Turn Your Garmin Data Into a Private AI Running Coach With a Local LLM
Most people using AI for training advice send their workout history to a cloud service, paste screenshots into a chat, and hope the model gives something useful back. It works, but it has two obvious downsides: privacy and consistency.
There is another way.
You can build a private training coach using a local LLM, a small Python ingestion pipeline, and your own workout data from Garmin. If you already run something like Rocket.Chat with an integrated local model, you can turn it into a personal coaching assistant that reads activity files, interprets trends, and gives weekly guidance without sending your fitness data anywhere.
This setup will not replace a real coach, and it will not magically understand physiology better than a sports scientist. But it can become a very useful layer for reviewing training load, spotting patterns, and keeping your plan honest.
Why use a local LLM instead of ChatGPT
The main reason is simple: control.
With a local setup, your training data stays on your hardware. That matters more than people think. Workout history can reveal a lot: health status, daily routine, recovery problems, location patterns, race preparation, body weight trends, and even stress periods.
A local model also gives you:
- privacy for health and routine data
- predictable behavior with a fixed prompt and workflow
- lower long-term cost if you already run the hardware
- easier integration with your own tools, databases, and chat systems
- freedom to tune the workflow for your exact sport and goals
If you already have Rocket.Chat running with a local model behind it, you are halfway there.
What the system should do
At a practical level, the system is simple.
You export Garmin data or sync files into a folder. A Python script reads those files, extracts useful training metrics, optionally reads screenshots, summarizes the data into a structured format, and passes that summary to a local LLM. The model then responds like a training assistant inside Rocket.Chat.
The LLM should not waste time parsing raw binary sensor files by itself. That is the Python script’s job. The model should receive a clean summary such as:
- weekly duration
- training frequency
- distance
- heart rate trends
- pace or power trends
- intensity distribution
- long-run consistency
- signs of fatigue
- unusual drops or spikes
- suggested next steps
That division of labor matters. The script handles facts. The model handles interpretation.
Supported Garmin inputs
A useful pipeline should support at least four input types.
FIT files — These are often the richest source of activity data. They may include heart rate, cadence, power, laps, GPS points, elevation, and device-generated metrics.
CSV exports — Good for bulk history and trend analysis. Easier to process and excellent for weekly summaries.
TCX files — Readable XML-based workout files, useful when you want lap or trackpoint-level data without dealing with FIT internals.
Screenshots — From Garmin Connect or your watch they can add context raw data may not capture cleanly: trend charts, training status, race predictions, or body battery summaries.
Recommended architecture
A clean local design looks like this:
1. Data sources
- Garmin FIT files
- Garmin CSV exports
- TCX workout files
- screenshots from Garmin Connect
- optional manual notes (soreness, sleep, weight, illness)
2. Python ingestion layer
This script should:
- detect file type
- parse FIT, CSV, and TCX
- extract key metrics
- normalize fields into one schema
- summarize by workout and by week
- optionally run OCR or image analysis on screenshots
- produce a structured JSON summary
3. Local storage
Store parsed results in:
- SQLite for simple use
- PostgreSQL if you want more scale
- plain JSON if you want minimalism
4. LLM layer
Use a local model served through:
- Ollama
- llama.cpp
- vLLM
- LocalAI
- or whatever sits behind your Rocket.Chat integration
5. Chat interface
Rocket.Chat becomes the front end. You ask:
- “Was my last week too hard?”
- “Am I ready for intervals tomorrow?”
- “Summarize my recovery trend over 4 weeks.”
- “Build my next 7-day plan.”
The assistant replies using the parsed data, not guesswork.
Why Python should do the heavy lifting
Many people make the mistake of throwing raw files at an LLM and asking it to figure everything out. That is lazy architecture.
A model is not the best parser for binary sports files. It is also not the most reliable way to compute load trends or compare heart rate drift across sessions.
Python is better for that.
Use Python for:
- file decoding
- metric extraction
- validation
- unit conversions
- trend calculations
- consistency checks
- handling missing fields
- generating summaries
Then feed the model something like:
{
"athlete": {
"weight_kg": 95,
"goal": "fat loss and strength with gradual aerobic improvement"
},
"week_summary": {
"sessions": 6,
"total_duration_min": 312,
"run_sessions": 2,
"swim_sessions": 2,
"gym_sessions": 2,
"avg_resting_hr": 61,
"avg_sleep_h": 6.7
},
"flags": [
"running frequency increased by 40 percent week over week",
"easy runs drifting into moderate intensity",
"sleep trending down for 5 days",
"higher HR at similar pace on last treadmill session"
],
"recommended_question": "Should this athlete deload, maintain, or progress next week?"
}That is what a model can actually reason over.
Python libraries worth using
A practical stack could look like this:
- fitparse or Garmin FIT SDK wrappers for FIT files
- pandas for CSV handling
- xml.etree.ElementTree or lxml for TCX
- pydantic for clean schemas
- sqlite3 or SQLAlchemy for storage
- Pillow for screenshot preprocessing
- optional OCR for screenshot text extraction if needed
For screenshots, use OCR only when necessary. If the screenshot is simple and you control the source, parsing the underlying exported data is better than reading pixels.
Example workflow
A user drops new files into a folder:
data/inbox/
2026-04-10-run.fit
2026-04-11-swim.tcx
activities_export.csv
weekly-garmin-summary.pngA Python job runs:
python ingest_garmin.py --input ./data/inbox --output ./data/normalizedThat script:
- parses all activities
- computes per-workout metrics
- updates weekly aggregates
- extracts notable flags
- writes
latest_summary.json
Then a second step posts the summary to your local model through the Rocket.Chat integration.
Inside Rocket.Chat, you can ask:
Review my last 14 days and tell me if I am accumulating fatigue or just being lazy.
The model answers based on the structured summary and your coaching prompt.
What the coaching prompt should do
The prompt is where the assistant stops being a generic chatbot and starts acting like a useful training reviewer.
A good system prompt would say something like:
- prioritize recovery and injury prevention
- identify under-recovery patterns
- distinguish easy, moderate, and hard training
- call out training monotony
- avoid fake certainty
- tell the athlete whether to push, maintain, or deload
- produce practical next-step recommendations
A decent example:
You are a blunt but useful endurance and general fitness coach.
You receive structured training summaries from Garmin-derived data.
Your job is to:
- identify fatigue, inconsistency, overreaching, or undertraining
- interpret heart rate, pace, power, duration, frequency, and recovery signals
- highlight missing or unreliable data
- recommend the next 7 days of training
- keep the athlete from adding junk volume
Do not flatter. Do not hallucinate physiology metrics.
State uncertainty clearly.
Prefer practical recommendations over motivational language.That alone will improve output quality more than changing from one trendy model to another.
What the local model is actually good at
A local LLM will do well at:
- weekly training summaries
- identifying obvious patterns
- comparing recent weeks
- suggesting session structure
- turning data into readable coaching feedback
- adapting tone to your training style
It will do poorly at:
- replacing medical advice
- estimating hidden physiological values accurately
- interpreting bad or incomplete data without guardrails
- making high-confidence decisions from noisy screenshots alone
So keep the model in its lane.
Example coaching output
Once the pipeline is working, your assistant should be able to produce responses like:
You trained consistently, but your last three sessions show rising heart rate at similar easy pace and lower sleep. That does not scream collapse, but it does suggest accumulating fatigue. Keep one quality session this week, not two. Reduce total run volume by 15 to 20 percent, keep swimming easy, and avoid adding extra treadmill work just because the week looks light.
That is useful. It is specific. It is based on data. It does not pretend to be magic.
Why Rocket.Chat is a nice front end
Rocket.Chat makes sense for this because it already solves the human side of the workflow.
You get:
- a chat interface you already use
- history of prior coaching conversations
- easy file upload
- easy bot integration
- team or personal channel support
- self-hosted control
Instead of building a separate dashboard first, you can start with chat and only add visualizations later.
A simple setup is:
- create a private
#training-coachchannel - upload FIT, CSV, TCX, or screenshots
- let a bot trigger the parser
- store the summary
- send the summary to the local model
- post the answer back into the thread
That is enough to make it genuinely useful.
A minimal project structure
training-coach/
app/
ingest_garmin.py
parse_fit.py
parse_csv.py
parse_tcx.py
parse_screenshots.py
summarize.py
prompt_builder.py
llm_client.py
rocketchat_bot.py
data/
inbox/
processed/
summaries/
db.sqlite
config/
settings.yaml
requirements.txtThings worth tracking beyond Garmin
Garmin activity data is useful, but not complete.
The system gets much better if you also track:
- body weight
- sleep hours
- resting heart rate
- HRV if available
- soreness
- illness
- stress
- gym effort
- subjective session RPE
Without that, the model may misread a normal slow day as fatigue, or miss fatigue because the run data alone looks fine.
The real value of this setup
The point is not to build a fake supercoach.
The point is to build a private training review system that:
- understands your recent work
- gives consistent feedback
- helps you avoid stupid decisions
- works on your infrastructure
- can evolve over time
That is already a meaningful win.
If you are the kind of person who already self-hosts Rocket.Chat, runs local models, and automates infrastructure, this is one of the more practical AI side projects you can build. It is personal, useful, and much less gimmicky than most “AI fitness” products.
Final thought
Cloud AI is convenient. Local AI is yours.
If you combine Garmin exports, a Python parser, structured summaries, and a local model behind Rocket.Chat, you can build a training assistant that is private, customizable, and actually grounded in your own data.
Just do not make the model parse raw chaos and call it intelligence. Let Python do the plumbing, let the LLM do the interpretation, and keep the feedback honest.