Files
go-garth-cli/python-garmin-connect/DailySummary.go
2025-09-21 11:03:52 -07:00

190 lines
9.2 KiB
Go

package connect
import (
"fmt"
"time"
)
// DateValue is a numeric value recorded on a given date.
type DateValue struct {
Date Date `json:"calendarDate"`
Value float64 `json:"value"`
}
// DailySummaries provides a daily summary of various statistics for multiple
// days.
type DailySummaries struct {
Start time.Time `json:"statisticsStartDate"`
End time.Time `json:"statisticsEndDate"`
TotalSteps []DateValue `json:"WELLNESS_TOTAL_STEPS"`
ActiveCalories []DateValue `json:"COMMON_ACTIVE_CALORIES"`
FloorsAscended []DateValue `json:"WELLNESS_FLOORS_ASCENDED"`
IntensityMinutes []DateValue `json:"WELLNESS_USER_INTENSITY_MINUTES_GOAL"`
MaxHeartRate []DateValue `json:"WELLNESS_MAX_HEART_RATE"`
MinimumAverageHeartRate []DateValue `json:"WELLNESS_MIN_AVG_HEART_RATE"`
MinimumHeartrate []DateValue `json:"WELLNESS_MIN_HEART_RATE"`
AverageStress []DateValue `json:"WELLNESS_AVERAGE_STRESS"`
RestingHeartRate []DateValue `json:"WELLNESS_RESTING_HEART_RATE"`
MaxStress []DateValue `json:"WELLNESS_MAX_STRESS"`
AbnormalHeartRateAlers []DateValue `json:"WELLNESS_ABNORMALHR_ALERTS_COUNT"`
MaximumAverageHeartRate []DateValue `json:"WELLNESS_MAX_AVG_HEART_RATE"`
StepGoal []DateValue `json:"WELLNESS_TOTAL_STEP_GOAL"`
FlorsAscendedGoal []DateValue `json:"WELLNESS_USER_FLOORS_ASCENDED_GOAL"`
ModerateIntensityMinutes []DateValue `json:"WELLNESS_MODERATE_INTENSITY_MINUTES"`
TotalColaries []DateValue `json:"WELLNESS_TOTAL_CALORIES"`
BodyBatteryCharged []DateValue `json:"WELLNESS_BODYBATTERY_CHARGED"`
FloorsDescended []DateValue `json:"WELLNESS_FLOORS_DESCENDED"`
BMRCalories []DateValue `json:"WELLNESS_BMR_CALORIES"`
FoodCaloriesRemainin []DateValue `json:"FOOD_CALORIES_REMAINING"`
TotalCalories []DateValue `json:"COMMON_TOTAL_CALORIES"`
BodyBatteryDrained []DateValue `json:"WELLNESS_BODYBATTERY_DRAINED"`
AverageSteps []DateValue `json:"WELLNESS_AVERAGE_STEPS"`
VigorousIntensifyMinutes []DateValue `json:"WELLNESS_VIGOROUS_INTENSITY_MINUTES"`
WellnessDistance []DateValue `json:"WELLNESS_TOTAL_DISTANCE"`
Distance []DateValue `json:"COMMON_TOTAL_DISTANCE"`
WellnessActiveCalories []DateValue `json:"WELLNESS_ACTIVE_CALORIES"`
}
// DailySummary is an extensive summary for a single day.
type DailySummary struct {
ProfileID int64 `json:"userProfileId"`
TotalKilocalories float64 `json:"totalKilocalories"`
ActiveKilocalories float64 `json:"activeKilocalories"`
BMRKilocalories float64 `json:"bmrKilocalories"`
WellnessKilocalories float64 `json:"wellnessKilocalories"`
BurnedKilocalories float64 `json:"burnedKilocalories"`
ConsumedKilocalories float64 `json:"consumedKilocalories"`
RemainingKilocalories float64 `json:"remainingKilocalories"`
TotalSteps int `json:"totalSteps"`
NetCalorieGoal float64 `json:"netCalorieGoal"`
TotalDistanceMeters int `json:"totalDistanceMeters"`
WellnessDistanceMeters int `json:"wellnessDistanceMeters"`
WellnessActiveKilocalories float64 `json:"wellnessActiveKilocalories"`
NetRemainingKilocalories float64 `json:"netRemainingKilocalories"`
UserID int64 `json:"userDailySummaryId"`
Date Date `json:"calendarDate"`
UUID string `json:"uuid"`
StepGoal int `json:"dailyStepGoal"`
StartTimeGMT Time `json:"wellnessStartTimeGmt"`
EndTimeGMT Time `json:"wellnessEndTimeGmt"`
StartLocal Time `json:"wellnessStartTimeLocal"`
EndLocal Time `json:"wellnessEndTimeLocal"`
Duration time.Duration `json:"durationInMilliseconds"`
Description string `json:"wellnessDescription"`
HighlyActive time.Duration `json:"highlyActiveSeconds"`
Active time.Duration `json:"activeSeconds"`
Sedentary time.Duration `json:"sedentarySeconds"`
Sleeping time.Duration `json:"sleepingSeconds"`
IncludesWellnessData bool `json:"includesWellnessData"`
IncludesActivityData bool `json:"includesActivityData"`
IncludesCalorieConsumedData bool `json:"includesCalorieConsumedData"`
PrivacyProtected bool `json:"privacyProtected"`
ModerateIntensity time.Duration `json:"moderateIntensityMinutes"`
VigorousIntensity time.Duration `json:"vigorousIntensityMinutes"`
FloorsAscendedInMeters float64 `json:"floorsAscendedInMeters"`
FloorsDescendedInMeters float64 `json:"floorsDescendedInMeters"`
FloorsAscended float64 `json:"floorsAscended"`
FloorsDescended float64 `json:"floorsDescended"`
IntensityGoal time.Duration `json:"intensityMinutesGoal"`
FloorsAscendedGoal int `json:"userFloorsAscendedGoal"`
MinHeartRate int `json:"minHeartRate"`
MaxHeartRate int `json:"maxHeartRate"`
RestingHeartRate int `json:"restingHeartRate"`
LastSevenDaysAvgRestingHeartRate int `json:"lastSevenDaysAvgRestingHeartRate"`
Source string `json:"source"`
AverageStress int `json:"averageStressLevel"`
MaxStress int `json:"maxStressLevel"`
Stress time.Duration `json:"stressDuration"`
RestStress time.Duration `json:"restStressDuration"`
ActivityStress time.Duration `json:"activityStressDuration"`
UncategorizedStress time.Duration `json:"uncategorizedStressDuration"`
TotalStress time.Duration `json:"totalStressDuration"`
LowStress time.Duration `json:"lowStressDuration"`
MediumStress time.Duration `json:"mediumStressDuration"`
HighStress time.Duration `json:"highStressDuration"`
StressQualifier string `json:"stressQualifier"`
MeasurableAwake time.Duration `json:"measurableAwakeDuration"`
MeasurableAsleep time.Duration `json:"measurableAsleepDuration"`
LastSyncGMT Time `json:"lastSyncTimestampGMT"`
MinAverageHeartRate int `json:"minAvgHeartRate"`
MaxAverageHeartRate int `json:"maxAvgHeartRate"`
}
// DailySummary will retrieve a detailed daily summary for date. If
// displayName is empty, the currently authenticated user will be used.
func (c *Client) DailySummary(displayName string, date time.Time) (*DailySummary, error) {
if displayName == "" && c.Profile == nil {
return nil, ErrNotAuthenticated
}
if displayName == "" && c.Profile != nil {
displayName = c.Profile.DisplayName
}
URL := fmt.Sprintf("https://connect.garmin.com/modern/proxy/usersummary-service/usersummary/daily/%s?calendarDate=%s",
displayName,
formatDate(date),
)
summary := new(DailySummary)
err := c.getJSON(URL, summary)
if err != nil {
return nil, err
}
summary.Duration *= time.Millisecond
summary.HighlyActive *= time.Second
summary.Active *= time.Second
summary.Sedentary *= time.Second
summary.Sleeping *= time.Second
summary.ModerateIntensity *= time.Minute
summary.VigorousIntensity *= time.Minute
summary.IntensityGoal *= time.Minute
summary.Stress *= time.Second
summary.RestStress *= time.Second
summary.ActivityStress *= time.Second
summary.UncategorizedStress *= time.Second
summary.TotalStress *= time.Second
summary.LowStress *= time.Second
summary.MediumStress *= time.Second
summary.HighStress *= time.Second
summary.MeasurableAwake *= time.Second
summary.MeasurableAsleep *= time.Second
return summary, nil
}
// DailySummaries will retrieve a daily summary for userID.
func (c *Client) DailySummaries(userID string, from time.Time, until time.Time) (*DailySummaries, error) {
URL := fmt.Sprintf("https://connect.garmin.com/modern/proxy/userstats-service/wellness/daily/%s?fromDate=%s&untilDate=%s",
userID,
formatDate(from),
formatDate(until),
)
if !c.authenticated() {
return nil, ErrNotAuthenticated
}
// We use a proxy object to deserialize the values to proper Go types.
var proxy struct {
Start Date `json:"statisticsStartDate"`
End Date `json:"statisticsEndDate"`
AllMetrics struct {
Summary DailySummaries `json:"metricsMap"`
} `json:"allMetrics"`
}
err := c.getJSON(URL, &proxy)
if err != nil {
return nil, err
}
ret := &proxy.AllMetrics.Summary
ret.Start = proxy.Start.Time()
ret.End = proxy.End.Time()
return ret, nil
}