Files
GarminSync/internal/garmin/client.go
2025-08-08 07:21:20 -07:00

107 lines
2.7 KiB
Go

package garmin
import (
"fmt"
"os"
"time"
garminconnect "github.com/abrander/garmin-connect"
"github.com/sstent/garminsync/internal/config"
)
// Client represents a Garmin Connect API client
type Client struct {
client *garminconnect.Client
cfg *config.Config
lastAuth time.Time
}
const (
defaultSessionTimeout = 30 * time.Minute
)
// NewClient creates a new Garmin Connect client
func NewClient(cfg *config.Config) (*Client, error) {
// Create client with session persistence
client := garminconnect.NewClient(garminconnect.Credentials(cfg.GarminEmail, cfg.GarminPassword))
client.SessionFile = cfg.SessionPath
// Attempt to load existing session
if err := client.Authenticate(); err != nil {
return nil, fmt.Errorf("authentication failed: %w", err)
}
return &Client{
client: client,
cfg: cfg,
lastAuth: time.Now(),
}, nil
}
// checkSession checks if session is still valid, refreshes if expired
func (c *Client) checkSession() error {
timeout := c.cfg.SessionTimeout
if timeout == 0 {
timeout = defaultSessionTimeout
}
if time.Since(c.lastAuth) > timeout {
if err := c.client.Authenticate(); err != nil {
return fmt.Errorf("session refresh failed: %w", err)
}
c.lastAuth = time.Now()
}
return nil
}
// GetActivities retrieves activities from Garmin Connect
func (c *Client) GetActivities() ([]Activity, error) {
// Check and refresh session if needed
if err := c.checkSession(); err != nil {
return nil, err
}
// Get activities from Garmin Connect
garminActivities, err := c.client.Activities("", 0, 100) // Empty string = current user
if err != nil {
return nil, fmt.Errorf("failed to get activities: %w", err)
}
// Convert to our Activity struct
var activities []Activity
for _, ga := range garminActivities {
activities = append(activities, Activity{
ActivityId: int(ga.ID),
StartTime: time.Time(ga.StartLocal),
Filename: fmt.Sprintf("activity_%d_%s.fit", ga.ID, ga.StartLocal.Time().Format("20060102")),
Downloaded: false,
})
}
return activities, nil
}
// DownloadActivityFIT downloads a specific FIT file
func (c *Client) DownloadActivityFIT(activityId int, filename string) error {
// Check and refresh session if needed
if err := c.checkSession(); err != nil {
return err
}
// Apply rate limiting
time.Sleep(c.cfg.RateLimit)
// Create file for writing
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer file.Close()
// Download FIT file
if err := c.client.ExportActivity(activityId, file, garminconnect.ActivityFormatFIT); err != nil {
return fmt.Errorf("failed to export activity %d: %w", activityId, err)
}
return nil
}