Files
go-garth/python-garmin-connect/Weight.go
2025-09-20 15:21:49 -07:00

168 lines
4.4 KiB
Go

package connect
import (
"fmt"
"time"
)
// Weightin is a single weight event.
type Weightin struct {
Date Date `json:"date"`
Version int `json:"version"`
Weight float64 `json:"weight"` // gram
BMI float64 `json:"bmi"` // weight / height²
BodyFatPercentage float64 `json:"bodyFat"` // percent
BodyWater float64 `json:"bodyWater"` // kilogram
BoneMass int `json:"boneMass"` // gram
MuscleMass int `json:"muscleMass"` // gram
SourceType string `json:"sourceType"`
}
// WeightAverage is aggregated weight data for a specific period.
type WeightAverage struct {
Weightin
From int `json:"from"`
Until int `json:"until"`
}
// LatestWeight will retrieve the latest weight by date.
func (c *Client) LatestWeight(date time.Time) (*Weightin, error) {
URL := fmt.Sprintf("https://connect.garmin.com/modern/proxy/weight-service/weight/latest?date=%04d-%02d-%02d",
date.Year(),
date.Month(),
date.Day())
wi := new(Weightin)
err := c.getJSON(URL, wi)
if err != nil {
return nil, err
}
return wi, nil
}
// Weightins will retrieve all weight ins between startDate and endDate. A
// summary is provided as well. This summary is calculated by Garmin Connect.
func (c *Client) Weightins(startDate time.Time, endDate time.Time) (*WeightAverage, []Weightin, error) {
URL := fmt.Sprintf("https://connect.garmin.com/modern/proxy/weight-service/weight/dateRange?startDate=%s&endDate=%s",
formatDate(startDate),
formatDate(endDate))
// An alternative endpoint for weight info this can be found here:
// https://connect.garmin.com/modern/proxy/userprofile-service/userprofile/personal-information/weightWithOutbound?from=1556359100000&until=1556611800000
if !c.authenticated() {
return nil, nil, ErrNotAuthenticated
}
var proxy struct {
DateWeightList []Weightin `json:"dateWeightList"`
TotalAverage *WeightAverage `json:"totalAverage"`
}
err := c.getJSON(URL, &proxy)
if err != nil {
return nil, nil, err
}
return proxy.TotalAverage, proxy.DateWeightList, nil
}
// DeleteWeightin will delete all biometric data for date.
func (c *Client) DeleteWeightin(date time.Time) error {
URL := fmt.Sprintf("https://connect.garmin.com/modern/proxy/biometric-service/biometric/%s", formatDate(date))
if !c.authenticated() {
return ErrNotAuthenticated
}
return c.write("DELETE", URL, nil, 204)
}
// AddUserWeight will add a manual weight in. weight is in grams to match
// Weightin.
func (c *Client) AddUserWeight(date time.Time, weight float64) error {
URL := "https://connect.garmin.com/modern/proxy/weight-service/user-weight"
payload := struct {
Date string `json:"date"`
UnitKey string `json:"unitKey"`
Value float64 `json:"value"`
}{
Date: formatDate(date),
UnitKey: "kg",
Value: weight / 1000.0,
}
return c.write("POST", URL, payload, 204)
}
// WeightByDate retrieves the weight of date if available. If no weight data
// for date exists, it will return ErrNotFound.
func (c *Client) WeightByDate(date time.Time) (Time, float64, error) {
URL := fmt.Sprintf("https://connect.garmin.com/modern/proxy/biometric-service/biometric/weightByDate?date=%s",
formatDate(date))
if !c.authenticated() {
return Time{}, 0.0, ErrNotAuthenticated
}
var proxy []struct {
TimeStamp Time `json:"weightDate"`
Weight float64 `json:"weight"` // gram
}
err := c.getJSON(URL, &proxy)
if err != nil {
return Time{}, 0.0, err
}
if len(proxy) < 1 {
return Time{}, 0.0, ErrNotFound
}
return proxy[0].TimeStamp, proxy[0].Weight, nil
}
// WeightGoal will list the users weight goal if any. If displayName is empty,
// the currently authenticated user will be used.
func (c *Client) WeightGoal(displayName string) (*Goal, error) {
goals, err := c.Goals(displayName, 4)
if err != nil {
return nil, err
}
if len(goals) < 1 {
return nil, ErrNotFound
}
return &goals[0], nil
}
// SetWeightGoal will set a new weight goal.
func (c *Client) SetWeightGoal(goal int) error {
if !c.authenticated() || c.Profile == nil {
return ErrNotAuthenticated
}
g := Goal{
Created: Today(),
Start: Today(),
GoalType: 4,
ProfileID: c.Profile.ProfileID,
Value: goal,
}
goals, err := c.Goals("", 4)
if err != nil {
return err
}
if len(goals) >= 1 {
g.ID = goals[0].ID
return c.UpdateGoal("", g)
}
return c.AddGoal(c.Profile.DisplayName, g)
}