mirror of
https://github.com/sstent/go-garth.git
synced 2026-01-25 16:42:28 +00:00
sync
This commit is contained in:
42
examples/activities/.gitignore
vendored
Normal file
42
examples/activities/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Build artifacts
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool
|
||||
*.out
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# Local data files
|
||||
data/
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
# Sensitive files
|
||||
*.key
|
||||
*.pem
|
||||
config/secrets.json
|
||||
113
examples/activities/README.md
Normal file
113
examples/activities/README.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Garmin Connect Activity Examples
|
||||
|
||||
This directory contains examples demonstrating how to use the go-garth library to interact with Garmin Connect activities.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. A Garmin Connect account
|
||||
2. Your Garmin Connect username and password
|
||||
3. Go 1.22 or later
|
||||
|
||||
## Setup
|
||||
|
||||
1. Create a `.env` file in this directory with your credentials:
|
||||
```
|
||||
GARMIN_USERNAME=your_username
|
||||
GARMIN_PASSWORD=your_password
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Activity Listing (`activities_example.go`)
|
||||
|
||||
This example demonstrates basic authentication and activity listing functionality:
|
||||
|
||||
- Authenticates with Garmin Connect
|
||||
- Lists recent activities
|
||||
- Shows basic activity information
|
||||
|
||||
Run with:
|
||||
```bash
|
||||
go run activities_example.go
|
||||
```
|
||||
|
||||
### Enhanced Activity Listing (`enhanced_example.go`)
|
||||
|
||||
This example provides more comprehensive activity querying capabilities:
|
||||
|
||||
- Lists recent activities
|
||||
- Filters activities by date range
|
||||
- Filters activities by activity type (e.g., running, cycling)
|
||||
- Searches activities by name
|
||||
- Retrieves detailed activity information including:
|
||||
- Elevation data
|
||||
- Heart rate metrics
|
||||
- Speed metrics
|
||||
- Step counts
|
||||
|
||||
Run with:
|
||||
```bash
|
||||
go run enhanced_example.go
|
||||
```
|
||||
|
||||
## Activity Types
|
||||
|
||||
Common activity types you can filter by:
|
||||
- `running`
|
||||
- `cycling`
|
||||
- `walking`
|
||||
- `swimming`
|
||||
- `strength_training`
|
||||
- `hiking`
|
||||
- `yoga`
|
||||
|
||||
## Output Examples
|
||||
|
||||
### Basic Listing
|
||||
```
|
||||
=== RECENT ACTIVITIES ===
|
||||
Recent Activities (5 total):
|
||||
==================================================
|
||||
1. Morning Run [running]
|
||||
Date: 2024-01-15 07:30
|
||||
Distance: 5.20 km
|
||||
Duration: 28m
|
||||
Calories: 320
|
||||
|
||||
2. Evening Ride [cycling]
|
||||
Date: 2024-01-14 18:00
|
||||
Distance: 15.50 km
|
||||
Duration: 45m
|
||||
Calories: 280
|
||||
```
|
||||
|
||||
### Detailed Activity Information
|
||||
```
|
||||
=== DETAILS FOR ACTIVITY: Morning Run ===
|
||||
Activity ID: 123456789
|
||||
Name: Morning Run
|
||||
Type: running
|
||||
Description: Easy morning run in the park
|
||||
Start Time: 2024-01-15 07:30:00
|
||||
Distance: 5.20 km
|
||||
Duration: 28m
|
||||
Calories: 320
|
||||
Elevation Gain: 45 m
|
||||
Elevation Loss: 42 m
|
||||
Max Heart Rate: 165 bpm
|
||||
Avg Heart Rate: 142 bpm
|
||||
Max Speed: 12.5 km/h
|
||||
Avg Speed: 11.1 km/h
|
||||
Steps: 5200
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Never commit your `.env` file to version control
|
||||
- The `.env` file is already included in `.gitignore`
|
||||
- Consider using environment variables directly in production instead of `.env` files
|
||||
@@ -13,8 +13,17 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load environment variables from .env file
|
||||
if err := godotenv.Load("../../../.env"); err != nil {
|
||||
// Load environment variables from .env file (robust path handling)
|
||||
var envPath string
|
||||
if _, err := os.Stat("../../.env"); err == nil {
|
||||
envPath = "../../.env"
|
||||
} else if _, err := os.Stat("../../../.env"); err == nil {
|
||||
envPath = "../../../.env"
|
||||
} else {
|
||||
envPath = ".env"
|
||||
}
|
||||
|
||||
if err := godotenv.Load(envPath); err != nil {
|
||||
log.Println("Note: Using system environment variables (no .env file found)")
|
||||
}
|
||||
|
||||
|
||||
194
examples/activities/enhanced/main.go
Normal file
194
examples/activities/enhanced/main.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/sstent/go-garth"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load environment variables from .env file (robust path handling)
|
||||
var envPath string
|
||||
if _, err := os.Stat("../../../.env"); err == nil {
|
||||
envPath = "../../../.env"
|
||||
} else if _, err := os.Stat("../../.env"); err == nil {
|
||||
envPath = "../../.env"
|
||||
} else {
|
||||
envPath = ".env"
|
||||
}
|
||||
|
||||
if err := godotenv.Load(envPath); err != nil {
|
||||
log.Println("Note: Using system environment variables (no .env file found)")
|
||||
}
|
||||
|
||||
// Get credentials from environment
|
||||
username := os.Getenv("GARMIN_USERNAME")
|
||||
password := os.Getenv("GARMIN_PASSWORD")
|
||||
if username == "" || password == "" {
|
||||
log.Fatal("GARMIN_USERNAME or GARMIN_PASSWORD not set in environment")
|
||||
}
|
||||
|
||||
// Create context with timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Create token storage and authenticator
|
||||
storage := garth.NewMemoryStorage()
|
||||
auth := garth.NewAuthenticator(garth.ClientOptions{
|
||||
Storage: storage,
|
||||
TokenURL: "https://connectapi.garmin.com/oauth-service/oauth/token",
|
||||
Timeout: 30 * time.Second,
|
||||
})
|
||||
|
||||
// Authenticate
|
||||
token, err := auth.Login(ctx, username, password, "")
|
||||
if err != nil {
|
||||
log.Fatalf("Authentication failed: %v", err)
|
||||
}
|
||||
log.Printf("Authenticated successfully! Token expires at: %s", token.Expiry.Format(time.RFC3339))
|
||||
|
||||
// Create HTTP client with authentication transport
|
||||
httpClient := &http.Client{
|
||||
Transport: garth.NewAuthTransport(auth.(*garth.GarthAuthenticator), storage, nil),
|
||||
}
|
||||
|
||||
// Create API client
|
||||
apiClient := garth.NewAPIClient("https://connectapi.garmin.com", httpClient)
|
||||
|
||||
// Create activity service
|
||||
activityService := garth.NewActivityService(apiClient)
|
||||
|
||||
// Example 1: List recent activities
|
||||
fmt.Println("\n=== RECENT ACTIVITIES ===")
|
||||
recentActivities, err := activityService.List(ctx, garth.ActivityListOptions{
|
||||
Limit: 10,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to get recent activities: %v", err)
|
||||
} else {
|
||||
printActivities(recentActivities, "Recent Activities")
|
||||
}
|
||||
|
||||
// Example 2: List activities from last 30 days
|
||||
fmt.Println("\n=== ACTIVITIES FROM LAST 30 DAYS ===")
|
||||
thirtyDaysAgo := time.Now().AddDate(0, 0, -30)
|
||||
recentMonthActivities, err := activityService.List(ctx, garth.ActivityListOptions{
|
||||
StartDate: thirtyDaysAgo,
|
||||
EndDate: time.Now(),
|
||||
Limit: 20,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to get activities from last 30 days: %v", err)
|
||||
} else {
|
||||
printActivities(recentMonthActivities, "Last 30 Days")
|
||||
}
|
||||
|
||||
// Example 3: List running activities only
|
||||
fmt.Println("\n=== RUNNING ACTIVITIES ===")
|
||||
runningActivities, err := activityService.List(ctx, garth.ActivityListOptions{
|
||||
ActivityType: "running",
|
||||
Limit: 15,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to get running activities: %v", err)
|
||||
} else {
|
||||
printActivities(runningActivities, "Running Activities")
|
||||
}
|
||||
|
||||
// Example 4: Search activities by name
|
||||
fmt.Println("\n=== ACTIVITIES CONTAINING 'MORNING' ===")
|
||||
morningActivities, err := activityService.List(ctx, garth.ActivityListOptions{
|
||||
NameContains: "morning",
|
||||
Limit: 10,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to search activities: %v", err)
|
||||
} else {
|
||||
printActivities(morningActivities, "Activities with 'morning' in name")
|
||||
}
|
||||
|
||||
// Example 5: Get detailed information for the first activity
|
||||
if len(recentActivities) > 0 {
|
||||
firstActivity := recentActivities[0]
|
||||
fmt.Printf("\n=== DETAILS FOR ACTIVITY: %s ===\n", firstActivity.Name)
|
||||
|
||||
details, err := activityService.Get(ctx, firstActivity.ActivityID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get activity details: %v", err)
|
||||
} else {
|
||||
printActivityDetails(details)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("\nEnhanced activity listing example completed successfully!")
|
||||
}
|
||||
|
||||
func printActivities(activities []garth.Activity, title string) {
|
||||
if len(activities) == 0 {
|
||||
fmt.Printf("No %s found\n", title)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s (%d total):\n", title, len(activities))
|
||||
fmt.Println(strings.Repeat("=", 50))
|
||||
|
||||
for i, activity := range activities {
|
||||
fmt.Printf("%d. %s [%s]\n", i+1, activity.Name, activity.Type)
|
||||
fmt.Printf(" Date: %s\n", activity.StartTime.Format("2006-01-02 15:04"))
|
||||
fmt.Printf(" Distance: %.2f km\n", activity.Distance/1000)
|
||||
fmt.Printf(" Duration: %s\n", formatDuration(activity.Duration))
|
||||
fmt.Printf(" Calories: %d\n", activity.Calories)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func printActivityDetails(details *garth.ActivityDetails) {
|
||||
fmt.Printf("Activity ID: %d\n", details.ActivityID)
|
||||
fmt.Printf("Name: %s\n", details.Name)
|
||||
fmt.Printf("Type: %s\n", details.Type)
|
||||
fmt.Printf("Description: %s\n", details.Description)
|
||||
fmt.Printf("Start Time: %s\n", details.StartTime.Format("2006-01-02 15:04:05"))
|
||||
fmt.Printf("Distance: %.2f km\n", details.Distance/1000)
|
||||
fmt.Printf("Duration: %s\n", formatDuration(details.Duration))
|
||||
fmt.Printf("Calories: %d\n", details.Calories)
|
||||
|
||||
if details.ElevationGain > 0 {
|
||||
fmt.Printf("Elevation Gain: %.0f m\n", details.ElevationGain)
|
||||
}
|
||||
if details.ElevationLoss > 0 {
|
||||
fmt.Printf("Elevation Loss: %.0f m\n", details.ElevationLoss)
|
||||
}
|
||||
if details.MaxHeartRate > 0 {
|
||||
fmt.Printf("Max Heart Rate: %d bpm\n", details.MaxHeartRate)
|
||||
}
|
||||
if details.AvgHeartRate > 0 {
|
||||
fmt.Printf("Avg Heart Rate: %d bpm\n", details.AvgHeartRate)
|
||||
}
|
||||
if details.MaxSpeed > 0 {
|
||||
fmt.Printf("Max Speed: %.1f km/h\n", details.MaxSpeed*3.6)
|
||||
}
|
||||
if details.AvgSpeed > 0 {
|
||||
fmt.Printf("Avg Speed: %.1f km/h\n", details.AvgSpeed*3.6)
|
||||
}
|
||||
if details.Steps > 0 {
|
||||
fmt.Printf("Steps: %d\n", details.Steps)
|
||||
}
|
||||
}
|
||||
|
||||
func formatDuration(seconds float64) string {
|
||||
duration := time.Duration(seconds * float64(time.Second))
|
||||
hours := int(duration.Hours())
|
||||
minutes := int(duration.Minutes()) % 60
|
||||
|
||||
if hours > 0 {
|
||||
return fmt.Sprintf("%dh %dm", hours, minutes)
|
||||
}
|
||||
return fmt.Sprintf("%dm", minutes)
|
||||
}
|
||||
@@ -5,4 +5,6 @@ go 1.22
|
||||
require (
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/sstent/go-garth v0.1.0
|
||||
)
|
||||
)
|
||||
|
||||
replace github.com/sstent/go-garth => ../../
|
||||
|
||||
2
examples/activities/go.sum
Normal file
2
examples/activities/go.sum
Normal file
@@ -0,0 +1,2 @@
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
@@ -1,80 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/sstent/go-garth"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Load environment variables from .env file
|
||||
if err := godotenv.Load("../../.env"); err != nil {
|
||||
log.Println("Note: Using system environment variables (no .env file found)")
|
||||
}
|
||||
|
||||
// Get credentials from environment
|
||||
username := os.Getenv("GARMIN_USERNAME")
|
||||
password := os.Getenv("GARMIN_PASSWORD")
|
||||
if username == "" || password == "" {
|
||||
log.Fatal("GARMIN_USERNAME or GARMIN_PASSWORD not set in environment")
|
||||
}
|
||||
|
||||
// Create context with timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Create token storage and authenticator
|
||||
storage := garth.NewMemoryStorage()
|
||||
auth := garth.NewAuthenticator(garth.ClientOptions{
|
||||
Storage: storage,
|
||||
TokenURL: "https://connectapi.garmin.com/oauth-service/oauth/token",
|
||||
Timeout: 30 * time.Second,
|
||||
})
|
||||
|
||||
// Authenticate
|
||||
token, err := auth.Login(ctx, username, password, "")
|
||||
if err != nil {
|
||||
log.Fatalf("Authentication failed: %v", err)
|
||||
}
|
||||
log.Printf("Authenticated successfully! Token expires at: %s", token.Expiry.Format(time.RFC3339))
|
||||
|
||||
// Create HTTP client with authentication transport
|
||||
httpClient := &http.Client{
|
||||
Transport: garth.NewAuthTransport(auth.(*garth.GarthAuthenticator), storage, nil),
|
||||
}
|
||||
|
||||
// Create API client
|
||||
apiClient := garth.NewAPIClient("https://connectapi.garmin.com", httpClient)
|
||||
|
||||
// Create activity service
|
||||
activityService := garth.NewActivityService(apiClient)
|
||||
|
||||
// List last 20 activities
|
||||
activities, err := activityService.List(ctx, garth.ActivityListOptions{
|
||||
Limit: 20,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get activities: %v", err)
|
||||
}
|
||||
|
||||
// Print activities
|
||||
fmt.Println("\nLast 20 Activities:")
|
||||
fmt.Println("=======================================")
|
||||
for i, activity := range activities {
|
||||
fmt.Printf("%d. %s [%s] - %s\n", i+1,
|
||||
activity.Name,
|
||||
activity.Type,
|
||||
activity.StartTime.Format("2006-01-02 15:04"))
|
||||
fmt.Printf(" Distance: %.2f km, Duration: %.0f min\n\n",
|
||||
activity.Distance/1000,
|
||||
activity.Duration/60)
|
||||
}
|
||||
|
||||
fmt.Println("Example completed successfully!")
|
||||
}
|
||||
2
go.mod
2
go.mod
@@ -2,4 +2,4 @@ module github.com/sstent/go-garth
|
||||
|
||||
go 1.22
|
||||
|
||||
require github.com/joho/godotenv v1.5.1 // indirect
|
||||
require github.com/joho/godotenv v1.5.1
|
||||
|
||||
BIN
login_page_1756913368.html
Normal file
BIN
login_page_1756913368.html
Normal file
Binary file not shown.
BIN
login_page_1756918964.html
Normal file
BIN
login_page_1756918964.html
Normal file
Binary file not shown.
BIN
login_page_1756918983.html
Normal file
BIN
login_page_1756918983.html
Normal file
Binary file not shown.
BIN
login_page_1756919005.html
Normal file
BIN
login_page_1756919005.html
Normal file
Binary file not shown.
BIN
login_page_1756919015.html
Normal file
BIN
login_page_1756919015.html
Normal file
Binary file not shown.
BIN
login_page_1756919019.html
Normal file
BIN
login_page_1756919019.html
Normal file
Binary file not shown.
BIN
login_page_1756922746.html
Normal file
BIN
login_page_1756922746.html
Normal file
Binary file not shown.
Reference in New Issue
Block a user