feat: Implement Phase 1B: Enhanced CLI Commands

This commit is contained in:
2025-09-18 14:02:37 -07:00
parent b47ff34d5a
commit 2870d23fed
10 changed files with 1036 additions and 134 deletions

View File

@@ -1 +1,38 @@
package garmin
package garmin
import (
"time"
)
// ActivityOptions for filtering activity lists
type ActivityOptions struct {
Limit int
Offset int
ActivityType string
DateFrom time.Time
DateTo time.Time
}
// ActivityDetail represents detailed information for an activity
type ActivityDetail struct {
Activity // Embed garmin.Activity from pkg/garmin/types.go
Description string `json:"description"` // Add more fields as needed
}
// Lap represents a lap in an activity
type Lap struct {
// Define lap fields
}
// Metric represents a metric in an activity
type Metric struct {
// Define metric fields
}
// DownloadOptions for downloading activity data
type DownloadOptions struct {
Format string // "gpx", "tcx", "fit", "csv"
Original bool // Download original uploaded file
OutputDir string
Filename string
}

View File

@@ -1,8 +1,14 @@
package garmin
import (
"fmt"
"time"
internalClient "garmin-connect/internal/api/client"
"garmin-connect/internal/types"
"garmin-connect/pkg/garmin/activities"
"garmin-connect/pkg/garmin/health"
"garmin-connect/pkg/garmin/stats"
)
// Client is the main Garmin Connect client type
@@ -16,7 +22,7 @@ func NewClient(domain string) (*Client, error) {
if err != nil {
return nil, err
}
return &Client{Client: c}, nil
return &Client{Client: c},
}
// Login authenticates to Garmin Connect
@@ -34,9 +40,92 @@ func (c *Client) SaveSession(filename string) error {
return c.Client.SaveSession(filename)
}
// RefreshSession refreshes the authentication tokens
func (c *Client) RefreshSession() error {
return c.Client.RefreshSession()
}
// GetActivities retrieves recent activities
func (c *Client) GetActivities(limit int) ([]Activity, error) {
return c.Client.GetActivities(limit)
func (c *Client) GetActivities(opts activities.ActivityOptions) ([]Activity, error) {
// TODO: Map ActivityOptions to internalClient.Client.GetActivities parameters
// For now, just call the internal client's GetActivities with a dummy limit
internalActivities, err := c.Client.GetActivities(opts.Limit)
if err != nil {
return nil, err
}
var garminActivities []Activity
for _, act := range internalActivities {
garminActivities = append(garminActivities, Activity{
ActivityID: act.ActivityID,
ActivityName: act.ActivityName,
ActivityType: act.ActivityType,
Starttime: act.Starttime,
Distance: act.Distance,
Duration: act.Duration,
})
}
return garminActivities, nil
}
// GetActivity retrieves details for a specific activity ID
func (c *Client) GetActivity(activityID int) (*activities.ActivityDetail, error) {
// TODO: Implement internalClient.Client.GetActivity
return nil, fmt.Errorf("not implemented")
}
// DownloadActivity downloads activity data
func (c *Client) DownloadActivity(activityID int, opts activities.DownloadOptions) error {
// TODO: Implement internalClient.Client.DownloadActivity
return fmt.Errorf("not implemented")
}
// SearchActivities searches for activities by a query string
func (c *Client) SearchActivities(query string) ([]Activity, error) {
// TODO: Implement internalClient.Client.SearchActivities
return nil, fmt.Errorf("not implemented")
}
// GetSleepData retrieves sleep data for a specified date range
func (c *Client) GetSleepData(startDate, endDate time.Time) ([]health.SleepData, error) {
// TODO: Implement internalClient.Client.GetSleepData
return nil, fmt.Errorf("not implemented")
}
// GetHrvData retrieves HRV data for a specified number of days
func (c *Client) GetHrvData(days int) ([]health.HrvData, error) {
// TODO: Implement internalClient.Client.GetHrvData
return nil, fmt.Errorf("not implemented")
}
// GetStressData retrieves stress data
func (c *Client) GetStressData(startDate, endDate time.Time) ([]health.StressData, error) {
// TODO: Implement internalClient.Client.GetStressData
return nil, fmt.Errorf("not implemented")
}
// GetBodyBatteryData retrieves Body Battery data
func (c *Client) GetBodyBatteryData(startDate, endDate time.Time) ([]health.BodyBatteryData, error) {
// TODO: Implement internalClient.Client.GetBodyBatteryData
return nil, fmt.Errorf("not implemented")
}
// GetStepsData retrieves steps data for a specified date range
func (c *Client) GetStepsData(startDate, endDate time.Time) ([]stats.StepsData, error) {
// TODO: Implement internalClient.Client.GetStepsData
return nil, fmt.Errorf("not implemented")
}
// GetDistanceData retrieves distance data for a specified date range
func (c *Client) GetDistanceData(startDate, endDate time.Time) ([]stats.DistanceData, error) {
// TODO: Implement internalClient.Client.GetDistanceData
return nil, fmt.Errorf("not implemented")
}
// GetCaloriesData retrieves calories data for a specified date range
func (c *Client) GetCaloriesData(startDate, endDate time.Time) ([]stats.CaloriesData, error) {
// TODO: Implement internalClient.Client.GetCaloriesData
return nil, fmt.Errorf("not implemented")
}
// OAuth1Token returns the OAuth1 token

View File

@@ -1,58 +1,41 @@
package garmin
import (
"garmin-connect/internal/data"
"time"
)
// BodyBatteryData represents Body Battery data.
type BodyBatteryData = data.DailyBodyBatteryStress
// SleepData represents sleep data.
type SleepData = data.DailySleepDTO
// HRVData represents HRV data.
type HRVData = data.HRVData
// WeightData represents weight data.
type WeightData = data.WeightData
// GetBodyBattery retrieves Body Battery data for a given date.
func (c *Client) GetBodyBattery(date time.Time) (*BodyBatteryData, error) {
bb := &data.DailyBodyBatteryStress{}
result, err := bb.Get(date, c.Client)
if err != nil {
return nil, err
}
return result.(*BodyBatteryData), nil
// SleepData represents sleep summary data
type SleepData struct {
Date time.Time `json:"calendarDate"`
SleepScore int `json:"sleepScore"`
TotalSleepSeconds int `json:"totalSleepSeconds"`
DeepSleepSeconds int `json:"deepSleepSeconds"`
LightSleepSeconds int `json:"lightSleepSeconds"`
RemSleepSeconds int `json:"remSleepSeconds"`
AwakeSleepSeconds int `json:"awakeSleepSeconds"`
// Add more fields as needed
}
// GetSleep retrieves sleep data for a given date.
func (c *Client) GetSleep(date time.Time) (*SleepData, error) {
sleep := &data.DailySleepDTO{}
result, err := sleep.Get(date, c.Client)
if err != nil {
return nil, err
}
return result.(*SleepData), nil
// HrvData represents Heart Rate Variability data
type HrvData struct {
Date time.Time `json:"calendarDate"`
HrvValue float64 `json:"hrvValue"`
// Add more fields as needed
}
// GetHRV retrieves HRV data for a given date.
func (c *Client) GetHRV(date time.Time) (*HRVData, error) {
hrv := &data.HRVData{}
result, err := hrv.Get(date, c.Client)
if err != nil {
return nil, err
}
return result.(*HRVData), nil
// StressData represents stress level data
type StressData struct {
Date time.Time `json:"calendarDate"`
StressLevel int `json:"stressLevel"`
RestStressLevel int `json:"restStressLevel"`
// Add more fields as needed
}
// GetWeight retrieves weight data for a given date.
func (c *Client) GetWeight(date time.Time) (*WeightData, error) {
weight := &data.WeightData{}
result, err := weight.Get(date, c.Client)
if err != nil {
return nil, err
}
return result.(*WeightData), nil
}
// BodyBatteryData represents Body Battery data
type BodyBatteryData struct {
Date time.Time `json:"calendarDate"`
BatteryLevel int `json:"batteryLevel"`
Charge int `json:"charge"`
Drain int `json:"drain"`
// Add more fields as needed
}

View File

@@ -1,6 +1,8 @@
package garmin
import (
"time"
"garmin-connect/internal/stats"
)
@@ -36,3 +38,21 @@ func NewDailySleep() Stats {
func NewDailyHRV() Stats {
return stats.NewDailyHRV()
}
// StepsData represents steps statistics
type StepsData struct {
Date time.Time `json:"calendarDate"`
Steps int `json:"steps"`
}
// DistanceData represents distance statistics
type DistanceData struct {
Date time.Time `json:"calendarDate"`
Distance float64 `json:"distance"` // in meters
}
// CaloriesData represents calories statistics
type CaloriesData struct {
Date time.Time `json:"calendarDate"`
Calories int `json:"activeCalories"`
}