0.3.11 with curl_cffi as optional tls extra (FreeBSD support)
This commit is contained in:
558
PKG-INFO
Normal file
558
PKG-INFO
Normal file
@ -0,0 +1,558 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: garminconnect
|
||||
Version: 0.3.11
|
||||
Summary: Python 3 API wrapper for Garmin Connect
|
||||
Keywords: garmin connect,api,garmin
|
||||
Author-Email: Ron Klinkien <ron@cyberjunky.nl>
|
||||
License: MIT
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: MacOS :: MacOS X
|
||||
Classifier: Operating System :: Microsoft :: Windows
|
||||
Classifier: Operating System :: POSIX :: Linux
|
||||
Classifier: Operating System :: OS Independent
|
||||
Project-URL: Homepage, https://github.com/cyberjunky/python-garminconnect
|
||||
Project-URL: Issues, https://github.com/cyberjunky/python-garminconnect/issues
|
||||
Project-URL: Changelog, https://github.com/cyberjunky/python-garminconnect/releases
|
||||
Requires-Python: >=3.12
|
||||
Requires-Dist: curl_cffi>=0.15.0
|
||||
Requires-Dist: requests>=2.33.0
|
||||
Requires-Dist: ua-generator>=1.0
|
||||
Provides-Extra: dev
|
||||
Requires-Dist: ipython; extra == "dev"
|
||||
Requires-Dist: ipdb; extra == "dev"
|
||||
Requires-Dist: ipykernel; extra == "dev"
|
||||
Requires-Dist: pandas; extra == "dev"
|
||||
Requires-Dist: matplotlib; extra == "dev"
|
||||
Provides-Extra: workout
|
||||
Requires-Dist: pydantic>=2.4.0; extra == "workout"
|
||||
Provides-Extra: typed
|
||||
Requires-Dist: pydantic>=2.4.0; extra == "typed"
|
||||
Provides-Extra: security
|
||||
Requires-Dist: bandit[toml]; extra == "security"
|
||||
Requires-Dist: pip-audit; extra == "security"
|
||||
Provides-Extra: linting
|
||||
Requires-Dist: black[jupyter]; extra == "linting"
|
||||
Requires-Dist: ruff; extra == "linting"
|
||||
Requires-Dist: mypy; extra == "linting"
|
||||
Requires-Dist: isort; extra == "linting"
|
||||
Requires-Dist: types-requests; extra == "linting"
|
||||
Requires-Dist: pre-commit; extra == "linting"
|
||||
Requires-Dist: codespell; extra == "linting"
|
||||
Provides-Extra: testing
|
||||
Requires-Dist: coverage; extra == "testing"
|
||||
Requires-Dist: pytest; extra == "testing"
|
||||
Requires-Dist: pytest-vcr>=1.0.2; extra == "testing"
|
||||
Requires-Dist: vcrpy>=7.0.0; extra == "testing"
|
||||
Provides-Extra: example
|
||||
Requires-Dist: readchar; extra == "example"
|
||||
Description-Content-Type: text/markdown
|
||||
|
||||
[![GitHub Release][releases-shield]][releases]
|
||||
[![GitHub Activity][commits-shield]][commits]
|
||||
[![License][license-shield]](LICENSE)
|
||||
![Project Maintenance][maintenance-shield]
|
||||
|
||||
[](https://www.paypal.me/cyberjunkynl/)
|
||||
[](https://github.com/sponsors/cyberjunky)
|
||||
|
||||
# Python: Garmin Connect
|
||||
|
||||
The Garmin Connect API library comes with two examples:
|
||||
|
||||
- **`example.py`** - Simple getting-started example showing authentication, token storage, and basic API calls
|
||||
- **`demo.py`** - Comprehensive demo providing access to **130+ API methods** organized into **13 categories** for easy navigation
|
||||
|
||||
```bash
|
||||
$ ./demo.py
|
||||
📁 Exported data will be saved to the directory: 'your_data'
|
||||
📄 All API responses are written to: 'response.json'
|
||||
Attempting to login using stored tokens from: ~/.garminconnect
|
||||
Successfully logged in using stored tokens!
|
||||
|
||||
📊 Your Stats Today: 4,045 steps | 1445.0 kcal
|
||||
🐌 Time to get those legs moving!
|
||||
|
||||
==================================================
|
||||
🚴 Full-blown Garmin Connect API Demo - Main Menu
|
||||
==================================================
|
||||
Select a category:
|
||||
|
||||
[1] 👤 User & Profile
|
||||
[2] 📊 Daily Health & Activity
|
||||
[3] 🔬 Advanced Health Metrics
|
||||
[4] 📈 Historical Data & Trends
|
||||
[5] 🏃 Activities & Workouts
|
||||
[6] ⚖️ Body Composition & Weight
|
||||
[7] 🏆 Goals & Achievements
|
||||
[8] ⌚ Device & Technical
|
||||
[9] 🎽 Gear & Equipment
|
||||
[0] 💧 Hydration & Wellness
|
||||
[a] 🔧 System & Export
|
||||
[b] 📅 Training Plans
|
||||
[c] ⛳ Golf
|
||||
[d] ✏️ Activity Editing
|
||||
|
||||
[q] Exit program
|
||||
|
||||
Make your selection:
|
||||
```
|
||||
|
||||
## API Coverage Statistics
|
||||
|
||||
- **Total API Methods**: 144+ unique endpoints (snapshot)
|
||||
- **Categories**: 13 organized sections
|
||||
- **User & Profile**: 4 methods (basic user info, settings)
|
||||
- **Daily Health & Activity**: 10 methods (today's health data plus daily calories, resting HR and sleep ranges)
|
||||
- **Advanced Health Metrics**: 16 methods (fitness metrics, HRV, VO2, FTP range, training readiness, training zones, running tolerance)
|
||||
- **Historical Data & Trends**: 9 methods (date range queries, weekly aggregates)
|
||||
- **Activities & Workouts**: 41 methods (comprehensive activity, workout management, typed workout uploads including strength, in-place edit, scheduling, push to device, import, edit description / exercise sets)
|
||||
- **Body Composition & Weight**: 8 methods (weight tracking, body composition)
|
||||
- **Goals & Achievements**: 15 methods (challenges, badges, goals)
|
||||
- **Device & Technical**: 7 methods (device info, settings)
|
||||
- **Gear & Equipment**: 7 methods (gear management, tracking)
|
||||
- **Hydration & Wellness**: 12 methods (hydration, nutrition, blood pressure, menstrual)
|
||||
- **System & Export**: 5 methods (reporting, logout, GraphQL, health snapshot download)
|
||||
- **Training Plans**: 3 methods (plans, plan by ID, adaptive plan by ID)
|
||||
- **Golf**: 5 methods (scorecard summary, scorecard detail, shot data, club stats, user stats)
|
||||
|
||||
### Interactive Features
|
||||
|
||||
- **Enhanced User Experience**: Categorized navigation with emoji indicators
|
||||
- **Smart Data Management**: Interactive weigh-in deletion with search capabilities
|
||||
- **Comprehensive Coverage**: All major Garmin Connect features are accessible
|
||||
- **Error Handling**: Robust error handling with user-friendly prompts
|
||||
- **Data Export**: JSON export functionality for all data types
|
||||
|
||||
[](https://www.paypal.me/cyberjunkynl/)
|
||||
[](https://github.com/sponsors/cyberjunky)
|
||||
|
||||
A comprehensive Python3 API wrapper for Garmin Connect, providing access to health, fitness, and device data.
|
||||
|
||||
## 📖 About
|
||||
|
||||
This library enables developers to programmatically access Garmin Connect data including:
|
||||
|
||||
- **Health Metrics**: Heart rate, sleep, stress, body composition, SpO2, HRV
|
||||
- **Activity Data**: Workouts, typed workout uploads (running, cycling, swimming, walking, hiking, strength), workout scheduling, exercises, training status, performance metrics, import-style uploads (no Strava re-export)
|
||||
- **Nutrition**: Daily food logs, meals, and nutrition settings
|
||||
- **Golf**: Scorecard summaries, scorecard details, shot-by-shot data
|
||||
- **Device Information**: Connected devices, settings, alarms, solar data
|
||||
- **Goals & Achievements**: Personal records, badges, challenges, race predictions
|
||||
- **Historical Data**: Trends, progress tracking, date range queries
|
||||
|
||||
Compatible with all Garmin Connect accounts. See <https://connect.garmin.com/>
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
Requires Python 3.12 or later.
|
||||
|
||||
Install from PyPI:
|
||||
|
||||
```bash
|
||||
pip install --upgrade garminconnect curl_cffi
|
||||
```
|
||||
|
||||
## Run demo software (recommended)
|
||||
|
||||
Clone the repo, then:
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv --copies
|
||||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
pip install -e ".[example]"
|
||||
|
||||
python3 ./example.py # simple getting-started example
|
||||
python3 ./demo.py # comprehensive demo (130+ API methods)
|
||||
```
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
This project uses [PDM](https://pdm.fming.dev/) for dependency management and task automation.
|
||||
|
||||
> **⚠️ Important**: Create a virtual environment first on externally-managed Python installs (Debian/Ubuntu) to avoid system package conflicts.
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv --copies
|
||||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
pip install pdm
|
||||
python3 -m pdm install --group :all
|
||||
pre-commit install --install-hooks # optional but recommended
|
||||
```
|
||||
|
||||
> **Note**: Using `python -m pdm` instead of `pdm` avoids PATH issues on some
|
||||
> Windows setups where `pip install pdm` places the `pdm` executable outside
|
||||
> the directories on `PATH`. Once `pdm install` has run, subsequent `pdm run ...`
|
||||
> commands work normally because the venv's `Scripts/` directory is on `PATH`
|
||||
> while the venv is active.
|
||||
|
||||
**Development commands:**
|
||||
|
||||
```bash
|
||||
pdm run format # Auto-format code (isort, black, ruff --fix)
|
||||
pdm run lint # Check code quality (isort, ruff, black, mypy)
|
||||
pdm run codespell # Check spelling
|
||||
pdm run test # Run test suite
|
||||
pdm run testcov # Run tests with coverage report
|
||||
pdm run all # Run all checks (lint + codespell + pre-commit + test)
|
||||
pdm run clean # Clean build artifacts and cache files
|
||||
pdm run build # Build package for distribution
|
||||
pdm run publish # Build and publish to PyPI
|
||||
pdm run --list # Show all available commands
|
||||
```
|
||||
|
||||
Run `pdm run format && pdm run lint && pdm run test` before submitting PRs.
|
||||
|
||||
## 🔐 Authentication
|
||||
|
||||
Authentication uses the same mobile SSO flow as the official Garmin Connect Android app.
|
||||
No browser is needed.
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. **First login**: Authenticates via `sso.garmin.com/mobile/api/login` using the Android
|
||||
app's client ID. If MFA is required, a callback (`prompt_mfa`) prompts for the one-time code.
|
||||
2. **Token exchange**: The service ticket is exchanged for DI OAuth Bearer tokens
|
||||
(`access_token` + `refresh_token`) via `diauth.garmin.com`. Tokens are stored at
|
||||
`~/.garminconnect/garmin_tokens.json`.
|
||||
3. **Auto-refresh**: Before each API request the library checks whether the DI token is about
|
||||
to expire and refreshes it automatically — no user interaction required.
|
||||
|
||||
**Session lifetime:**
|
||||
- DI tokens auto-refresh indefinitely as long as the refresh token remains valid.
|
||||
- A full re-login with credentials (and possibly MFA) is only needed if the refresh token
|
||||
itself expires or is revoked.
|
||||
|
||||
**Token storage:**
|
||||
```bash
|
||||
~/.garminconnect/garmin_tokens.json # saved automatically, mode 0600
|
||||
```
|
||||
|
||||
The containing directory is restricted to mode `0700`. Treat the token file
|
||||
like a password: the refresh token can provide persistent account access. Avoid
|
||||
putting a Garmin password in shell history or a long-lived environment variable;
|
||||
prefer `getpass()` or another interactive secret prompt.
|
||||
|
||||
**Resilient login (multi-strategy + token validation):**
|
||||
|
||||
`login()` tries several authentication strategies in order (mobile, SSO widget,
|
||||
web portal — each with and without TLS impersonation) and only declares success
|
||||
when the resulting token is actually accepted by the API. If a strategy obtains
|
||||
a token the API later rejects (a region/account-specific condition — see
|
||||
[#369](https://github.com/cyberjunky/python-garminconnect/issues/369)), the
|
||||
library transparently falls through to the next strategy. Set
|
||||
`Garmin(..., verify_login=False)` to restore the legacy "first token wins"
|
||||
behavior.
|
||||
|
||||
**Cached-token gotcha & self-healing:** when a `tokenstore` is supplied,
|
||||
`login()` loads those tokens *before* the strategy chain and short-circuits if
|
||||
they load — so stale/poisoned cached tokens used to fail every run. The library
|
||||
now detects this: if cached tokens are rejected by the API, it discards them and
|
||||
performs a fresh credential login automatically. To force a clean slate yourself
|
||||
(e.g. between a failed resume and a retry), call:
|
||||
|
||||
```python
|
||||
g.logout() # clears in-memory auth + cached tokens (uses GARMINTOKENS)
|
||||
g.logout(tokenstore) # or pass an explicit path
|
||||
```
|
||||
|
||||
`logout()` removes only the local `garmin_tokens.json` file and preserves its
|
||||
directory and unrelated files. It does **not** revoke a token that has already
|
||||
been issued by Garmin. Revoke account access from Garmin's account/security
|
||||
settings if a token may have been copied or exposed.
|
||||
|
||||
### What running it locally does
|
||||
|
||||
This is an unofficial client for Garmin's web services; it does not pair with
|
||||
the Garmin Connect phone app or connect directly to a watch. When you call
|
||||
`login()`, your credentials and MFA code are sent over HTTPS to Garmin's login
|
||||
service. The library receives an access/refresh token pair and, when a token
|
||||
store is supplied, caches it locally for later sessions. Subsequent API methods
|
||||
send that token to Garmin and can read or change the same account data that the
|
||||
selected method targets.
|
||||
|
||||
The library does not automatically download an entire account. The demo writes
|
||||
responses, activity downloads, and health reports only when you select those
|
||||
actions. Demo exports are stored under `your_data/` with owner-only directory
|
||||
and file permissions. Run the project in a dedicated virtual environment, read
|
||||
the method you plan to call, and start with read-only methods. Upload, edit,
|
||||
delete, schedule, hydration, and weigh-in methods can change Garmin account data.
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
The default suite is credential-free and excludes live-account integration tests:
|
||||
|
||||
```bash
|
||||
pdm run test # Run all tests
|
||||
pdm run testcov # Run tests with coverage report
|
||||
```
|
||||
|
||||
To explicitly run live integration tests, use a test account if possible. This
|
||||
can create local VCR recordings and includes methods that may mutate the account:
|
||||
|
||||
```bash
|
||||
export GARMIN_EMAIL="you@example.com"
|
||||
read -s GARMIN_PASSWORD && export GARMIN_PASSWORD
|
||||
pdm run pytest -m integration --vcr-record=once
|
||||
unset GARMIN_PASSWORD
|
||||
```
|
||||
|
||||
VCR recordings are ignored by Git because Garmin responses contain sensitive
|
||||
health, activity, location, and account data. Do not commit them.
|
||||
|
||||
## 📦 Publishing
|
||||
|
||||
For package maintainers:
|
||||
|
||||
**Setup PyPI credentials:**
|
||||
|
||||
```bash
|
||||
pip install twine
|
||||
# Edit with your preferred editor, or create via here-doc:
|
||||
# cat > ~/.pypirc <<'EOF'
|
||||
# [pypi]
|
||||
# username = __token__
|
||||
# password = <PyPI_API_TOKEN>
|
||||
# EOF
|
||||
```
|
||||
|
||||
```ini
|
||||
[pypi]
|
||||
username = __token__
|
||||
password = <PyPI_API_TOKEN>
|
||||
```
|
||||
|
||||
Recommended: use environment variables and restrict file perms
|
||||
|
||||
```bash
|
||||
chmod 600 ~/.pypirc
|
||||
export TWINE_USERNAME="__token__"
|
||||
export TWINE_PASSWORD="<PyPI_API_TOKEN>"
|
||||
```
|
||||
|
||||
**Publish new version:**
|
||||
|
||||
```bash
|
||||
pdm run publish # Build and publish to PyPI
|
||||
```
|
||||
|
||||
**Alternative publishing steps:**
|
||||
|
||||
```bash
|
||||
pdm run build # Build package only
|
||||
pdm publish # Publish pre-built package
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
We welcome contributions! Here's how you can help:
|
||||
|
||||
- **Report Issues**: Bug reports and feature requests via GitHub issues
|
||||
- **Submit PRs**: Code improvements, new features, documentation updates
|
||||
- **Testing**: Help test new features and report compatibility issues
|
||||
- **Documentation**: Improve examples, add use cases, fix typos
|
||||
|
||||
**Before contributing:**
|
||||
1. Set up your dev environment (see [Development](#️-development) above)
|
||||
2. Format and lint: `pdm run format && pdm run lint`
|
||||
3. Run tests: `pdm run test`
|
||||
4. Follow existing code style and patterns
|
||||
|
||||
### Jupyter Notebook
|
||||
|
||||
Explore the API interactively with our [reference notebook](https://github.com/cyberjunky/python-garminconnect/blob/master/docs/reference.ipynb).
|
||||
|
||||
### Python Code Examples
|
||||
|
||||
```python
|
||||
import os
|
||||
from getpass import getpass
|
||||
from datetime import date
|
||||
from garminconnect import Garmin
|
||||
|
||||
# First run: logs in and saves tokens to ~/.garminconnect
|
||||
# Subsequent runs: loads saved tokens and auto-refreshes
|
||||
client = Garmin(
|
||||
os.getenv("GARMIN_EMAIL"),
|
||||
getpass("Garmin password: "),
|
||||
prompt_mfa=lambda: input("MFA code: "),
|
||||
)
|
||||
client.login("~/.garminconnect")
|
||||
|
||||
# Get today's stats
|
||||
today = date.today().isoformat()
|
||||
stats = client.get_stats(today)
|
||||
|
||||
# Get heart rate data
|
||||
hr_data = client.get_heart_rates(today)
|
||||
print(f"Resting HR: {hr_data.get('restingHeartRate', 'n/a')}")
|
||||
```
|
||||
|
||||
### Typed Workouts (Pydantic Models)
|
||||
|
||||
The library includes optional typed workout models for creating type-safe workout definitions:
|
||||
|
||||
```bash
|
||||
pip install garminconnect[workout]
|
||||
```
|
||||
|
||||
```python
|
||||
from garminconnect.workout import (
|
||||
RunningWorkout,
|
||||
WorkoutSegment,
|
||||
create_warmup_step,
|
||||
create_interval_step,
|
||||
create_distance_interval_step,
|
||||
create_cooldown_step,
|
||||
create_repeat_group,
|
||||
)
|
||||
|
||||
# Create a structured running workout
|
||||
workout = RunningWorkout(
|
||||
workoutName="Easy Run",
|
||||
estimatedDurationInSecs=1800,
|
||||
workoutSegments=[
|
||||
WorkoutSegment(
|
||||
segmentOrder=1,
|
||||
sportType={"sportTypeId": 1, "sportTypeKey": "running"},
|
||||
workoutSteps=[create_warmup_step(300.0)],
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
# Upload and optionally schedule it
|
||||
result = client.upload_running_workout(workout)
|
||||
client.schedule_workout(result["workoutId"], "2026-03-20")
|
||||
|
||||
# Edit it in place - keeps its id, so any schedules pointing at it stay valid
|
||||
workout_data = client.get_workout_by_id(result["workoutId"])
|
||||
workout_data["workoutName"] = "Easy Run (revised)"
|
||||
client.update_workout(result["workoutId"], workout_data)
|
||||
|
||||
# Delete a workout or remove it from the calendar
|
||||
client.delete_workout(workout_id)
|
||||
client.unschedule_workout(scheduled_workout_id)
|
||||
|
||||
# Push a workout to a device - defaults to the last workout / last used device
|
||||
client.push_workout_to_device(result["workoutId"], device_id)
|
||||
```
|
||||
|
||||
**Available workout classes:** `RunningWorkout`, `CyclingWorkout`, `SwimmingWorkout`, `WalkingWorkout`, `HikingWorkout`, `StrengthWorkout`, `MultiSportWorkout`, `FitnessEquipmentWorkout`
|
||||
|
||||
**Strength workouts** are rep-based. Build each exercise with `create_strength_set(category, step_order, sets, reps, rest_seconds, exercise_name="", weight_kg=None)` and identify exercises with a `category` / `exercise` pair from the bundled catalog in `garminconnect.exercises` (1,527 exercises across 47 categories, with `resolve(name)` and `find(term)` helpers):
|
||||
|
||||
```python
|
||||
from garminconnect import exercises
|
||||
from garminconnect.workout import StrengthWorkout, WorkoutSegment, create_strength_set
|
||||
|
||||
lat = exercises.resolve(
|
||||
"Lat Pull-down"
|
||||
) # {'category': 'PULL_UP', 'exercise': 'LAT_PULLDOWN'}
|
||||
|
||||
workout = StrengthWorkout(
|
||||
workoutName="Upper Body",
|
||||
estimatedDurationInSecs=0,
|
||||
workoutSegments=[
|
||||
WorkoutSegment(
|
||||
segmentOrder=1,
|
||||
sportType={"sportTypeId": 5, "sportTypeKey": "strength_training"},
|
||||
workoutSteps=[
|
||||
create_strength_set(
|
||||
"BENCH_PRESS", step_order=1, sets=4, reps=10, rest_seconds=120
|
||||
),
|
||||
create_strength_set(
|
||||
lat["category"],
|
||||
step_order=4,
|
||||
sets=3,
|
||||
reps=12,
|
||||
rest_seconds=90,
|
||||
exercise_name=lat["exercise"],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
client.upload_strength_workout(workout)
|
||||
```
|
||||
|
||||
**Helper functions:** `create_warmup_step`, `create_interval_step`, `create_distance_interval_step`, `create_recovery_step`, `create_cooldown_step`, `create_repeat_group`, `create_strength_exercise_step`, `create_strength_rest_step`, `create_strength_set`
|
||||
|
||||
Use `create_distance_interval_step(600.0, step_order=1)` for interval steps that should end after a distance in meters instead of after a duration.
|
||||
|
||||
### Golf
|
||||
|
||||
The library supports reading Garmin Golf scorecards, shot-level data, club statistics, and golf user statistics.
|
||||
|
||||
```python
|
||||
# List recent golf scorecards
|
||||
summary = client.get_golf_summary(limit=10)
|
||||
scorecard_id = summary["scorecardSummaries"][0]["id"]
|
||||
|
||||
# Get the full scorecard details
|
||||
scorecard = client.get_golf_scorecard(scorecard_id)
|
||||
|
||||
# Get shot-by-shot data
|
||||
# - Omit hole_numbers to receive every hole (recommended for holes 10-18).
|
||||
# - Single-digit holes work as a comma- or dash-separated list, e.g. "1,2,3".
|
||||
# Commas are normalized to dashes because Garmin only accepts '-' as the
|
||||
# separator.
|
||||
# - Double-digit holes (10-18) cannot be fetched with a filter at all; the
|
||||
# API either drops them or returns an empty response. If you request holes
|
||||
# 10-18, the library falls back to returning all 18 holes.
|
||||
shots = client.get_golf_shot_data(scorecard_id)
|
||||
shots = client.get_golf_shot_data(scorecard_id, hole_numbers="1,2,3")
|
||||
|
||||
# Club and player statistics
|
||||
club_stats = client.get_golf_club_stats()
|
||||
user_stats = client.get_golf_user_stats()
|
||||
```
|
||||
|
||||
### Additional Resources
|
||||
- **Simple Example**: [example.py](https://raw.githubusercontent.com/cyberjunky/python-garminconnect/master/example.py) - Getting started guide
|
||||
- **Comprehensive Demo**: [demo.py](https://raw.githubusercontent.com/cyberjunky/python-garminconnect/master/demo.py) - All 130+ API methods
|
||||
- **API Documentation**: Comprehensive method documentation in source code
|
||||
- **Test Cases**: Real-world usage examples in `tests/` directory
|
||||
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
Special thanks to all contributors who have helped improve this project:
|
||||
|
||||
- **Community Contributors**: Bug reports, feature requests, and code improvements
|
||||
- **Issue Reporters**: Helping identify and resolve compatibility issues
|
||||
- **Feature Developers**: Adding new API endpoints and functionality
|
||||
- **Documentation Authors**: Improving examples and user guides
|
||||
|
||||
This project thrives thanks to community involvement and feedback.
|
||||
|
||||
## 💖 Support This Project
|
||||
|
||||
If you find this library useful for your projects, please consider supporting its continued development and maintenance:
|
||||
|
||||
### 🌟 Ways to Support
|
||||
|
||||
- **⭐ Star this repository** - Help others discover the project
|
||||
- **💰 Financial Support** - Contribute to development and hosting costs
|
||||
- **🐛 Report Issues** - Help improve stability and compatibility
|
||||
- **📖 Spread the Word** - Share with other developers
|
||||
|
||||
### 💳 Financial Support Options
|
||||
|
||||
[](https://www.paypal.me/cyberjunkynl/)
|
||||
[](https://github.com/sponsors/cyberjunky)
|
||||
|
||||
**Why Support?**
|
||||
- Keeps the project actively maintained
|
||||
- Enables faster bug fixes and new features
|
||||
- Supports infrastructure costs (testing, AI, CI/CD)
|
||||
- Shows appreciation for hundreds of hours of development
|
||||
|
||||
Every contribution, no matter the size, makes a difference and is greatly appreciated! 🙏
|
||||
|
||||
[releases-shield]: https://img.shields.io/github/release/cyberjunky/python-garminconnect.svg?style=for-the-badge
|
||||
[releases]: https://github.com/cyberjunky/python-garminconnect/releases
|
||||
[commits-shield]: https://img.shields.io/github/commit-activity/y/cyberjunky/python-garminconnect.svg?style=for-the-badge
|
||||
[commits]: https://github.com/cyberjunky/python-garminconnect/commits/master
|
||||
[license-shield]: https://img.shields.io/github/license/cyberjunky/python-garminconnect.svg?style=for-the-badge
|
||||
[maintenance-shield]: https://img.shields.io/badge/maintainer-cyberjunky-blue.svg?style=for-the-badge
|
||||
Reference in New Issue
Block a user