mirror of
https://github.com/sstent/go-garth.git
synced 2026-01-26 17:11:42 +00:00
sync - build broken
This commit is contained in:
1
python-garmin-connect/connect/.gitignore
vendored
Normal file
1
python-garmin-connect/connect/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/connect
|
||||
1
python-garmin-connect/connect/README.md
Normal file
1
python-garmin-connect/connect/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This is a simple CLI client for Garmin Connect.
|
||||
81
python-garmin-connect/connect/Table.go
Normal file
81
python-garmin-connect/connect/Table.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
columnsMax []int
|
||||
header []string
|
||||
rows [][]string
|
||||
}
|
||||
|
||||
func NewTable() *Table {
|
||||
return &Table{}
|
||||
}
|
||||
|
||||
func (t *Table) AddHeader(titles ...string) {
|
||||
t.header = titles
|
||||
t.columnsMax = make([]int, len(t.header))
|
||||
for i, title := range t.header {
|
||||
t.columnsMax[i] = utf8.RuneCountInString(title)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) AddRow(columns ...interface{}) {
|
||||
cols := sliceStringer(columns)
|
||||
|
||||
if len(columns) != len(t.header) {
|
||||
panic("worng number of columns")
|
||||
}
|
||||
|
||||
t.rows = append(t.rows, cols)
|
||||
|
||||
for i, col := range cols {
|
||||
l := utf8.RuneCountInString(col)
|
||||
|
||||
if t.columnsMax[i] < l {
|
||||
t.columnsMax[i] = l
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rightPad(in string, length int) string {
|
||||
result := in
|
||||
inLen := utf8.RuneCountInString(in)
|
||||
|
||||
for i := 0; i < length-inLen; i++ {
|
||||
result += " "
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (t *Table) outputLine(w io.Writer, columns []string) {
|
||||
line := ""
|
||||
|
||||
for i, column := range columns {
|
||||
line += rightPad(column, t.columnsMax[i]) + " "
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\n", line)
|
||||
}
|
||||
|
||||
func (t *Table) outputHeader(w io.Writer, columns []string) {
|
||||
line := ""
|
||||
|
||||
for i, column := range columns {
|
||||
line += "\033[1m" + rightPad(column, t.columnsMax[i]) + "\033[0m "
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\n", line)
|
||||
}
|
||||
|
||||
func (t *Table) Output(writer io.Writer) {
|
||||
t.outputHeader(writer, t.header)
|
||||
for _, row := range t.rows {
|
||||
t.outputLine(writer, row)
|
||||
}
|
||||
}
|
||||
63
python-garmin-connect/connect/Tabular.go
Normal file
63
python-garmin-connect/connect/Tabular.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Tabular struct {
|
||||
maxLength int
|
||||
titles []string
|
||||
values []Value
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Unit string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (v Value) String() string {
|
||||
str := stringer(v.Value)
|
||||
|
||||
return "\033[1m" + str + "\033[0m " + v.Unit
|
||||
}
|
||||
|
||||
func NewTabular() *Tabular {
|
||||
return &Tabular{}
|
||||
}
|
||||
|
||||
func (t *Tabular) AddValue(title string, value interface{}) {
|
||||
t.AddValueUnit(title, value, "")
|
||||
}
|
||||
|
||||
func (t *Tabular) AddValueUnit(title string, value interface{}, unit string) {
|
||||
v := Value{
|
||||
Unit: unit,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
t.titles = append(t.titles, title)
|
||||
t.values = append(t.values, v)
|
||||
|
||||
if len(title) > t.maxLength {
|
||||
t.maxLength = len(title)
|
||||
}
|
||||
}
|
||||
|
||||
func leftPad(in string, length int) string {
|
||||
result := ""
|
||||
inLen := utf8.RuneCountInString(in)
|
||||
|
||||
for i := 0; i < length-inLen; i++ {
|
||||
result += " "
|
||||
}
|
||||
|
||||
return result + in
|
||||
}
|
||||
|
||||
func (t *Tabular) Output(writer io.Writer) {
|
||||
for i, value := range t.values {
|
||||
fmt.Fprintf(writer, "%s %s\n", leftPad(t.titles[i], t.maxLength), value.String())
|
||||
}
|
||||
}
|
||||
217
python-garmin-connect/connect/activities.go
Normal file
217
python-garmin-connect/connect/activities.go
Normal file
@@ -0,0 +1,217 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
connect "github.com/abrander/garmin-connect"
|
||||
)
|
||||
|
||||
var (
|
||||
exportFormat string
|
||||
offset int
|
||||
count int
|
||||
)
|
||||
|
||||
func init() {
|
||||
activitiesCmd := &cobra.Command{
|
||||
Use: "activities",
|
||||
}
|
||||
rootCmd.AddCommand(activitiesCmd)
|
||||
|
||||
activitiesListCmd := &cobra.Command{
|
||||
Use: "list [display name]",
|
||||
Short: "List Activities",
|
||||
Run: activitiesList,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
activitiesListCmd.Flags().IntVarP(&offset, "offset", "o", 0, "Paginating index where the list starts from")
|
||||
activitiesListCmd.Flags().IntVarP(&count, "count", "c", 100, "Count of elements to return")
|
||||
activitiesCmd.AddCommand(activitiesListCmd)
|
||||
|
||||
activitiesViewCmd := &cobra.Command{
|
||||
Use: "view <activity id>",
|
||||
Short: "View details for an activity",
|
||||
Run: activitiesView,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
activitiesCmd.AddCommand(activitiesViewCmd)
|
||||
|
||||
activitiesViewWeatherCmd := &cobra.Command{
|
||||
Use: "weather <activity id>",
|
||||
Short: "View weather for an activity",
|
||||
Run: activitiesViewWeather,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
activitiesViewCmd.AddCommand(activitiesViewWeatherCmd)
|
||||
|
||||
activitiesViewHRZonesCmd := &cobra.Command{
|
||||
Use: "hrzones <activity id>",
|
||||
Short: "View hr zones for an activity",
|
||||
Run: activitiesViewHRZones,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
activitiesViewCmd.AddCommand(activitiesViewHRZonesCmd)
|
||||
|
||||
activitiesExportCmd := &cobra.Command{
|
||||
Use: "export <activity id>",
|
||||
Short: "Export an activity to a file",
|
||||
Run: activitiesExport,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
activitiesExportCmd.Flags().StringVarP(&exportFormat, "format", "f", "fit", "Format of export (fit, tcx, gpx, kml, csv)")
|
||||
activitiesCmd.AddCommand(activitiesExportCmd)
|
||||
|
||||
activitiesImportCmd := &cobra.Command{
|
||||
Use: "import <path>",
|
||||
Short: "Import an activity from a file",
|
||||
Run: activitiesImport,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
activitiesCmd.AddCommand(activitiesImportCmd)
|
||||
|
||||
activitiesDeleteCmd := &cobra.Command{
|
||||
Use: "delete <activity id>",
|
||||
Short: "Delete an activity",
|
||||
Run: activitiesDelete,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
activitiesCmd.AddCommand(activitiesDeleteCmd)
|
||||
|
||||
activitiesRenameCmd := &cobra.Command{
|
||||
Use: "rename <activity id> <new name>",
|
||||
Short: "Rename an activity",
|
||||
Run: activitiesRename,
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
activitiesCmd.AddCommand(activitiesRenameCmd)
|
||||
}
|
||||
|
||||
func activitiesList(_ *cobra.Command, args []string) {
|
||||
displayName := ""
|
||||
|
||||
if len(args) == 1 {
|
||||
displayName = args[0]
|
||||
}
|
||||
|
||||
activities, err := client.Activities(displayName, offset, count)
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Date", "Name", "Type", "Distance", "Time", "Avg/Max HR", "Calories")
|
||||
for _, a := range activities {
|
||||
t.AddRow(
|
||||
a.ID,
|
||||
a.StartLocal.Time,
|
||||
a.ActivityName,
|
||||
a.ActivityType.TypeKey,
|
||||
a.Distance,
|
||||
a.StartLocal,
|
||||
fmt.Sprintf("%.0f/%.0f", a.AverageHeartRate, a.MaxHeartRate),
|
||||
a.Calories,
|
||||
)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func activitiesView(_ *cobra.Command, args []string) {
|
||||
activityID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
activity, err := client.Activity(activityID)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("ID", activity.ID)
|
||||
t.AddValue("Name", activity.ActivityName)
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func activitiesViewWeather(_ *cobra.Command, args []string) {
|
||||
activityID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
weather, err := client.ActivityWeather(activityID)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValueUnit("Temperature", weather.Temperature, "°F")
|
||||
t.AddValueUnit("Apparent Temperature", weather.ApparentTemperature, "°F")
|
||||
t.AddValueUnit("Dew Point", weather.DewPoint, "°F")
|
||||
t.AddValueUnit("Relative Humidity", weather.RelativeHumidity, "%")
|
||||
t.AddValueUnit("Wind Direction", weather.WindDirection, weather.WindDirectionCompassPoint)
|
||||
t.AddValueUnit("Wind Speed", weather.WindSpeed, "mph")
|
||||
t.AddValue("Latitude", weather.Latitude)
|
||||
t.AddValue("Longitude", weather.Longitude)
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func activitiesViewHRZones(_ *cobra.Command, args []string) {
|
||||
activityID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
zones, err := client.ActivityHrZones(activityID)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
//for (zone in zones)
|
||||
for i := 0; i < len(zones)-1; i++ {
|
||||
t.AddValue(fmt.Sprintf("Zone %d (%3d-%3dbpm)", zones[i].ZoneNumber, zones[i].ZoneLowBoundary, zones[i+1].ZoneLowBoundary),
|
||||
zones[i].TimeInZone)
|
||||
}
|
||||
t.AddValue(fmt.Sprintf("Zone %d ( > %dbpm )", zones[len(zones)-1].ZoneNumber, zones[len(zones)-1].ZoneLowBoundary),
|
||||
zones[len(zones)-1].TimeInZone)
|
||||
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func activitiesExport(_ *cobra.Command, args []string) {
|
||||
format, err := connect.FormatFromExtension(exportFormat)
|
||||
bail(err)
|
||||
|
||||
activityID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
name := fmt.Sprintf("%d.%s", activityID, format.Extension())
|
||||
f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||||
bail(err)
|
||||
|
||||
err = client.ExportActivity(activityID, f, format)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func activitiesImport(_ *cobra.Command, args []string) {
|
||||
filename := args[0]
|
||||
|
||||
f, err := os.Open(filename)
|
||||
bail(err)
|
||||
|
||||
format, err := connect.FormatFromFilename(filename)
|
||||
bail(err)
|
||||
|
||||
id, err := client.ImportActivity(f, format)
|
||||
bail(err)
|
||||
|
||||
fmt.Printf("Activity ID %d imported\n", id)
|
||||
}
|
||||
|
||||
func activitiesDelete(_ *cobra.Command, args []string) {
|
||||
activityID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.DeleteActivity(activityID)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func activitiesRename(_ *cobra.Command, args []string) {
|
||||
activityID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
newName := args[1]
|
||||
|
||||
err = client.RenameActivity(activityID, newName)
|
||||
bail(err)
|
||||
}
|
||||
222
python-garmin-connect/connect/badges.go
Normal file
222
python-garmin-connect/connect/badges.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
connect "github.com/abrander/garmin-connect"
|
||||
)
|
||||
|
||||
const gotIt = "✓"
|
||||
|
||||
func init() {
|
||||
badgesCmd := &cobra.Command{
|
||||
Use: "badges",
|
||||
}
|
||||
rootCmd.AddCommand(badgesCmd)
|
||||
|
||||
badgesLeaderboardCmd := &cobra.Command{
|
||||
Use: "leaderboard",
|
||||
Short: "Show the current points leaderbaord among the authenticated users connections",
|
||||
Run: badgesLeaderboard,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
badgesCmd.AddCommand(badgesLeaderboardCmd)
|
||||
|
||||
badgesEarnedCmd := &cobra.Command{
|
||||
Use: "earned [display name]",
|
||||
Short: "Show the earned badges",
|
||||
Run: badgesEarned,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
badgesCmd.AddCommand(badgesEarnedCmd)
|
||||
|
||||
badgesAvailableCmd := &cobra.Command{
|
||||
Use: "available",
|
||||
Short: "Show badges not yet earned",
|
||||
Run: badgesAvailable,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
badgesCmd.AddCommand(badgesAvailableCmd)
|
||||
|
||||
badgesViewCmd := &cobra.Command{
|
||||
Use: "view <badge id>",
|
||||
Short: "Show details about a badge",
|
||||
Run: badgesView,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
badgesCmd.AddCommand(badgesViewCmd)
|
||||
|
||||
badgesCompareCmd := &cobra.Command{
|
||||
Use: "compare <display name>",
|
||||
Short: "Compare the authenticated users badges with the badges of another user",
|
||||
Run: badgesCompare,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
badgesCmd.AddCommand(badgesCompareCmd)
|
||||
}
|
||||
|
||||
func badgesLeaderboard(_ *cobra.Command, _ []string) {
|
||||
leaderboard, err := client.BadgeLeaderBoard()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("Display Name", "Name", "Level", "Points")
|
||||
for _, status := range leaderboard {
|
||||
t.AddRow(status.DisplayName, status.Fullname, status.Level, status.Point)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func badgesEarned(_ *cobra.Command, args []string) {
|
||||
var badges []connect.Badge
|
||||
|
||||
if len(args) == 1 {
|
||||
displayName := args[0]
|
||||
// If we have a displayid to show, we abuse the compare call to read
|
||||
// badges earned by a connection.
|
||||
_, status, err := client.BadgeCompare(displayName)
|
||||
bail(err)
|
||||
|
||||
badges = status.Badges
|
||||
} else {
|
||||
var err error
|
||||
badges, err = client.BadgesEarned()
|
||||
bail(err)
|
||||
}
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Badge", "Points", "Date")
|
||||
for _, badge := range badges {
|
||||
p := fmt.Sprintf("%d", badge.Points)
|
||||
if badge.EarnedNumber > 1 {
|
||||
p = fmt.Sprintf("%d x%d", badge.Points, badge.EarnedNumber)
|
||||
}
|
||||
t.AddRow(badge.ID, badge.Name, p, badge.EarnedDate.String())
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func badgesAvailable(_ *cobra.Command, _ []string) {
|
||||
badges, err := client.BadgesAvailable()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Key", "Name", "Points")
|
||||
for _, badge := range badges {
|
||||
t.AddRow(badge.ID, badge.Key, badge.Name, badge.Points)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func badgesView(_ *cobra.Command, args []string) {
|
||||
badgeID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
badge, err := client.BadgeDetail(badgeID)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("ID", badge.ID)
|
||||
t.AddValue("Key", badge.Key)
|
||||
t.AddValue("Name", badge.Name)
|
||||
t.AddValue("Points", badge.Points)
|
||||
t.AddValue("Earned", formatDate(badge.EarnedDate.Time))
|
||||
t.AddValueUnit("Earned", badge.EarnedNumber, "time(s)")
|
||||
t.AddValue("Available from", formatDate(badge.Start.Time))
|
||||
t.AddValue("Available to", formatDate(badge.End.Time))
|
||||
t.Output(os.Stdout)
|
||||
|
||||
if len(badge.Connections) > 0 {
|
||||
fmt.Printf("\n Connections with badge:\n")
|
||||
t := NewTable()
|
||||
t.AddHeader("Display Name", "Name", "Earned")
|
||||
for _, b := range badge.Connections {
|
||||
t.AddRow(b.DisplayName, b.FullName, b.EarnedDate.Time)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
if len(badge.RelatedBadges) > 0 {
|
||||
fmt.Printf("\n Relates badges:\n")
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Key", "Name", "Points", "Earned")
|
||||
for _, b := range badge.RelatedBadges {
|
||||
earned := ""
|
||||
if b.EarnedByMe {
|
||||
earned = gotIt
|
||||
}
|
||||
t.AddRow(b.ID, b.Key, b.Name, b.Points, earned)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
func badgesCompare(_ *cobra.Command, args []string) {
|
||||
displayName := args[0]
|
||||
a, b, err := client.BadgeCompare(displayName)
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("Badge", a.Fullname, b.Fullname, "Points")
|
||||
|
||||
type status struct {
|
||||
name string
|
||||
points int
|
||||
me bool
|
||||
meEarned int
|
||||
other bool
|
||||
otherEarned int
|
||||
}
|
||||
|
||||
m := map[string]*status{}
|
||||
|
||||
for _, badge := range a.Badges {
|
||||
s, found := m[badge.Key]
|
||||
if !found {
|
||||
s = &status{}
|
||||
m[badge.Key] = s
|
||||
}
|
||||
s.me = true
|
||||
s.meEarned = badge.EarnedNumber
|
||||
s.name = badge.Name
|
||||
s.points = badge.Points
|
||||
}
|
||||
|
||||
for _, badge := range b.Badges {
|
||||
s, found := m[badge.Key]
|
||||
if !found {
|
||||
s = &status{}
|
||||
m[badge.Key] = s
|
||||
}
|
||||
s.other = true
|
||||
s.otherEarned = badge.EarnedNumber
|
||||
s.name = badge.Name
|
||||
s.points = badge.Points
|
||||
}
|
||||
|
||||
for _, e := range m {
|
||||
var me string
|
||||
var other string
|
||||
if e.me {
|
||||
me = gotIt
|
||||
if e.meEarned > 1 {
|
||||
me += fmt.Sprintf(" %dx", e.meEarned)
|
||||
}
|
||||
}
|
||||
|
||||
if e.other {
|
||||
other = gotIt
|
||||
if e.otherEarned > 1 {
|
||||
other += fmt.Sprintf(" %dx", e.otherEarned)
|
||||
}
|
||||
}
|
||||
t.AddRow(e.name, me, other, e.points)
|
||||
}
|
||||
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
114
python-garmin-connect/connect/calendar.go
Normal file
114
python-garmin-connect/connect/calendar.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
calendarCmd := &cobra.Command{
|
||||
Use: "calendar",
|
||||
}
|
||||
rootCmd.AddCommand(calendarCmd)
|
||||
|
||||
calendarYearCmd := &cobra.Command{
|
||||
Use: "year <year>",
|
||||
Short: "List active days in the year",
|
||||
Run: calendarYear,
|
||||
Args: cobra.RangeArgs(1, 1),
|
||||
}
|
||||
calendarCmd.AddCommand(calendarYearCmd)
|
||||
|
||||
calendarMonthCmd := &cobra.Command{
|
||||
Use: "month <year> <month>",
|
||||
Short: "List active days in the month",
|
||||
Run: calendarMonth,
|
||||
Args: cobra.RangeArgs(2, 2),
|
||||
}
|
||||
calendarCmd.AddCommand(calendarMonthCmd)
|
||||
|
||||
calendarWeekCmd := &cobra.Command{
|
||||
Use: "week <year> <month> <day>",
|
||||
Short: "List active days in the week",
|
||||
Run: calendarWeek,
|
||||
Args: cobra.RangeArgs(3, 3),
|
||||
}
|
||||
calendarCmd.AddCommand(calendarWeekCmd)
|
||||
|
||||
}
|
||||
|
||||
func calendarYear(_ *cobra.Command, args []string) {
|
||||
year, err := strconv.ParseInt(args[0], 10, 32)
|
||||
bail(err)
|
||||
|
||||
calendar, err := client.CalendarYear(int(year))
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ActivityType ID", "Number of Activities", "Total Distance", "Total Duration", "Total Calories")
|
||||
for _, summary := range calendar.YearSummaries {
|
||||
t.AddRow(
|
||||
summary.ActivityTypeID,
|
||||
summary.NumberOfActivities,
|
||||
summary.TotalDistance,
|
||||
summary.TotalDuration,
|
||||
summary.TotalCalories,
|
||||
)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func calendarMonth(_ *cobra.Command, args []string) {
|
||||
year, err := strconv.ParseInt(args[0], 10, 32)
|
||||
bail(err)
|
||||
|
||||
month, err := strconv.ParseInt(args[1], 10, 32)
|
||||
bail(err)
|
||||
|
||||
calendar, err := client.CalendarMonth(int(year), int(month))
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Date", "Name", "Distance", "Time", "Calories")
|
||||
for _, item := range calendar.CalendarItems {
|
||||
t.AddRow(
|
||||
item.ID,
|
||||
item.Date,
|
||||
item.Title,
|
||||
item.Distance,
|
||||
item.ElapsedDuration,
|
||||
item.Calories,
|
||||
)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func calendarWeek(_ *cobra.Command, args []string) {
|
||||
year, err := strconv.ParseInt(args[0], 10, 32)
|
||||
bail(err)
|
||||
|
||||
month, err := strconv.ParseInt(args[1], 10, 32)
|
||||
bail(err)
|
||||
|
||||
week, err := strconv.ParseInt(args[2], 10, 32)
|
||||
bail(err)
|
||||
|
||||
calendar, err := client.CalendarWeek(int(year), int(month), int(week))
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Date", "Name", "Distance", "Time", "Calories")
|
||||
for _, item := range calendar.CalendarItems {
|
||||
t.AddRow(
|
||||
item.ID,
|
||||
item.Date,
|
||||
item.Title,
|
||||
item.Distance,
|
||||
item.ElapsedDuration,
|
||||
item.Calories,
|
||||
)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
169
python-garmin-connect/connect/challenges.go
Normal file
169
python-garmin-connect/connect/challenges.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
challengesCmd := &cobra.Command{
|
||||
Use: "challenges",
|
||||
}
|
||||
rootCmd.AddCommand(challengesCmd)
|
||||
|
||||
challengesListCmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List ad-hoc challenges",
|
||||
Run: challengesList,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
challengesCmd.AddCommand(challengesListCmd)
|
||||
|
||||
challengesListInvitesCmd := &cobra.Command{
|
||||
Use: "invites",
|
||||
Short: "List ad-hoc challenge invites",
|
||||
Run: challengesListInvites,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
challengesListCmd.AddCommand(challengesListInvitesCmd)
|
||||
|
||||
challengesAcceptCmd := &cobra.Command{
|
||||
Use: "accept <invation ID>",
|
||||
Short: "Accept an ad-hoc challenge",
|
||||
Run: challengesAccept,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
challengesCmd.AddCommand(challengesAcceptCmd)
|
||||
|
||||
challengesDeclineCmd := &cobra.Command{
|
||||
Use: "decline <invation ID>",
|
||||
Short: "Decline an ad-hoc challenge",
|
||||
Run: challengesDecline,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
challengesCmd.AddCommand(challengesDeclineCmd)
|
||||
|
||||
challengesListPreviousCmd := &cobra.Command{
|
||||
Use: "previous",
|
||||
Short: "Show completed ad-hoc challenges",
|
||||
Run: challengesListPrevious,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
challengesListCmd.AddCommand(challengesListPreviousCmd)
|
||||
|
||||
challengesViewCmd := &cobra.Command{
|
||||
Use: "view <id>",
|
||||
Short: "View challenge details",
|
||||
Run: challengesView,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
challengesCmd.AddCommand(challengesViewCmd)
|
||||
|
||||
challengesLeaveCmd := &cobra.Command{
|
||||
Use: "leave <challenge id>",
|
||||
Short: "Leave a challenge",
|
||||
Run: challengesLeave,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
challengesCmd.AddCommand(challengesLeaveCmd)
|
||||
|
||||
challengesRemoveCmd := &cobra.Command{
|
||||
Use: "remove <challenge id> <user id>",
|
||||
Short: "Remove a user from a challenge",
|
||||
Run: challengesRemove,
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
challengesCmd.AddCommand(challengesRemoveCmd)
|
||||
}
|
||||
|
||||
func challengesList(_ *cobra.Command, args []string) {
|
||||
challenges, err := client.AdhocChallenges()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Start", "End", "Description", "Name", "Rank")
|
||||
for _, c := range challenges {
|
||||
t.AddRow(c.UUID, c.Start, c.End, c.Description, c.Name, c.UserRanking)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func challengesListInvites(_ *cobra.Command, _ []string) {
|
||||
challenges, err := client.AdhocChallengeInvites()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("Invite ID", "Challenge ID", "Start", "End", "Description", "Name", "Rank")
|
||||
for _, c := range challenges {
|
||||
t.AddRow(c.InviteID, c.UUID, c.Start, c.End, c.Description, c.Name, c.UserRanking)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func challengesAccept(_ *cobra.Command, args []string) {
|
||||
inviteID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.AdhocChallengeInvitationRespond(inviteID, true)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func challengesDecline(_ *cobra.Command, args []string) {
|
||||
inviteID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.AdhocChallengeInvitationRespond(inviteID, false)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func challengesListPrevious(_ *cobra.Command, args []string) {
|
||||
challenges, err := client.HistoricalAdhocChallenges()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Start", "End", "Description", "Name", "Rank")
|
||||
for _, c := range challenges {
|
||||
t.AddRow(c.UUID, c.Start, c.End, c.Description, c.Name, c.UserRanking)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func challengesLeave(_ *cobra.Command, args []string) {
|
||||
uuid := args[0]
|
||||
err := client.LeaveAdhocChallenge(uuid, 0)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func challengesRemove(_ *cobra.Command, args []string) {
|
||||
uuid := args[0]
|
||||
|
||||
profileID, err := strconv.ParseInt(args[1], 10, 64)
|
||||
bail(err)
|
||||
|
||||
err = client.LeaveAdhocChallenge(uuid, profileID)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func challengesView(_ *cobra.Command, args []string) {
|
||||
uuid := args[0]
|
||||
challenge, err := client.AdhocChallenge(uuid)
|
||||
bail(err)
|
||||
|
||||
players := make([]string, len(challenge.Players))
|
||||
for i, player := range challenge.Players {
|
||||
players[i] = player.FullName + " [" + player.DisplayName + "]"
|
||||
}
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("ID", challenge.UUID)
|
||||
t.AddValue("Start", challenge.Start.String())
|
||||
t.AddValue("End", challenge.End.String())
|
||||
t.AddValue("Description", challenge.Description)
|
||||
t.AddValue("Name", challenge.Name)
|
||||
t.AddValue("Rank", challenge.UserRanking)
|
||||
t.AddValue("Players", strings.Join(players, ", "))
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
38
python-garmin-connect/connect/completion.go
Normal file
38
python-garmin-connect/connect/completion.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
completionCmd := &cobra.Command{
|
||||
Use: "completion",
|
||||
}
|
||||
rootCmd.AddCommand(completionCmd)
|
||||
|
||||
completionBashCmd := &cobra.Command{
|
||||
Use: "bash",
|
||||
Short: "Output command completion for Bourne Again Shell (bash)",
|
||||
RunE: completionBash,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
completionCmd.AddCommand(completionBashCmd)
|
||||
|
||||
completionZshCmd := &cobra.Command{
|
||||
Use: "zsh",
|
||||
Short: "Output command completion for Z Shell (zsh)",
|
||||
RunE: completionZsh,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
completionCmd.AddCommand(completionZshCmd)
|
||||
}
|
||||
|
||||
func completionBash(_ *cobra.Command, _ []string) error {
|
||||
return rootCmd.GenBashCompletion(os.Stdout)
|
||||
}
|
||||
|
||||
func completionZsh(_ *cobra.Command, _ []string) error {
|
||||
return rootCmd.GenZshCompletion(os.Stdout)
|
||||
}
|
||||
180
python-garmin-connect/connect/connections.go
Normal file
180
python-garmin-connect/connect/connections.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
connectionsCmd := &cobra.Command{
|
||||
Use: "connections",
|
||||
}
|
||||
rootCmd.AddCommand(connectionsCmd)
|
||||
|
||||
connectionsListCmd := &cobra.Command{
|
||||
Use: "list [display name]",
|
||||
Short: "List all connections",
|
||||
Run: connectionsList,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
connectionsCmd.AddCommand(connectionsListCmd)
|
||||
|
||||
connectionsPendingCmd := &cobra.Command{
|
||||
Use: "pending",
|
||||
Short: "List pending connections",
|
||||
Run: connectionsPending,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
connectionsCmd.AddCommand(connectionsPendingCmd)
|
||||
|
||||
connectionsRemoveCmd := &cobra.Command{
|
||||
Use: "remove <connection ID>",
|
||||
Short: "Remove a connection",
|
||||
Run: connectionsRemove,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
connectionsCmd.AddCommand(connectionsRemoveCmd)
|
||||
|
||||
connectionsSearchCmd := &cobra.Command{
|
||||
Use: "search <keyword>",
|
||||
Short: "Search Garmin wide for a person",
|
||||
Run: connectionsSearch,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
connectionsCmd.AddCommand(connectionsSearchCmd)
|
||||
|
||||
connectionsAcceptCmd := &cobra.Command{
|
||||
Use: "accept <request id>",
|
||||
Short: "Accept a connection request",
|
||||
Run: connectionsAccept,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
connectionsCmd.AddCommand(connectionsAcceptCmd)
|
||||
|
||||
connectionsRequestCmd := &cobra.Command{
|
||||
Use: "request <display name>",
|
||||
Short: "Request connectio from another user",
|
||||
Run: connectionsRequest,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
connectionsCmd.AddCommand(connectionsRequestCmd)
|
||||
|
||||
blockedCmd := &cobra.Command{
|
||||
Use: "blocked",
|
||||
}
|
||||
connectionsCmd.AddCommand(blockedCmd)
|
||||
|
||||
blockedListCmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List currently blocked users",
|
||||
Run: connectionsBlockedList,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
blockedCmd.AddCommand(blockedListCmd)
|
||||
|
||||
blockedAddCmd := &cobra.Command{
|
||||
Use: "add <display name>",
|
||||
Short: "Add a user to the blocked list",
|
||||
Run: connectionsBlockedAdd,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
blockedCmd.AddCommand(blockedAddCmd)
|
||||
|
||||
blockedRemoveCmd := &cobra.Command{
|
||||
Use: "remove <display name>",
|
||||
Short: "Remove a user from the blocked list",
|
||||
Run: connectionsBlockedRemove,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
blockedCmd.AddCommand(blockedRemoveCmd)
|
||||
}
|
||||
|
||||
func connectionsList(_ *cobra.Command, args []string) {
|
||||
displayName := ""
|
||||
if len(args) == 1 {
|
||||
displayName = args[0]
|
||||
}
|
||||
|
||||
connections, err := client.Connections(displayName)
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("Connection ID", "Display Name", "Name", "Location", "Profile Image")
|
||||
for _, c := range connections {
|
||||
t.AddRow(c.ConnectionRequestID, c.DisplayName, c.Fullname, c.Location, c.ProfileImageURLMedium)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func connectionsPending(_ *cobra.Command, _ []string) {
|
||||
connections, err := client.PendingConnections()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("RequestID", "Display Name", "Name", "Location", "Profile Image")
|
||||
for _, c := range connections {
|
||||
t.AddRow(c.ConnectionRequestID, c.DisplayName, c.Fullname, c.Location, c.ProfileImageURLMedium)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func connectionsRemove(_ *cobra.Command, args []string) {
|
||||
connectionRequestID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.RemoveConnection(connectionRequestID)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func connectionsSearch(_ *cobra.Command, args []string) {
|
||||
keyword := args[0]
|
||||
connections, err := client.SearchConnections(keyword)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
for _, c := range connections {
|
||||
t.AddValue(c.DisplayName, c.Fullname)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func connectionsAccept(_ *cobra.Command, args []string) {
|
||||
connectionRequestID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.AcceptConnection(connectionRequestID)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func connectionsRequest(_ *cobra.Command, args []string) {
|
||||
displayName := args[0]
|
||||
|
||||
err := client.RequestConnection(displayName)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func connectionsBlockedList(_ *cobra.Command, _ []string) {
|
||||
blockedUsers, err := client.BlockedUsers()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("Display Name", "Name", "Location", "Profile Image")
|
||||
for _, c := range blockedUsers {
|
||||
t.AddRow(c.DisplayName, c.Fullname, c.Location, c.ProfileImageURLMedium)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func connectionsBlockedAdd(_ *cobra.Command, args []string) {
|
||||
displayName := args[0]
|
||||
err := client.BlockUser(displayName)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func connectionsBlockedRemove(_ *cobra.Command, args []string) {
|
||||
displayName := args[0]
|
||||
err := client.UnblockUser(displayName)
|
||||
bail(err)
|
||||
}
|
||||
151
python-garmin-connect/connect/gear.go
Normal file
151
python-garmin-connect/connect/gear.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gearCmd := &cobra.Command{
|
||||
Use: "gear",
|
||||
}
|
||||
rootCmd.AddCommand(gearCmd)
|
||||
|
||||
gearListCmd := &cobra.Command{
|
||||
Use: "list [profile ID]",
|
||||
Short: "List Gear",
|
||||
Run: gearList,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
gearCmd.AddCommand(gearListCmd)
|
||||
|
||||
gearTypeListCmd := &cobra.Command{
|
||||
Use: "types",
|
||||
Short: "List Gear Types",
|
||||
Run: gearTypeList,
|
||||
}
|
||||
gearCmd.AddCommand(gearTypeListCmd)
|
||||
|
||||
gearLinkCommand := &cobra.Command{
|
||||
Use: "link <gear UUID> <activity id>",
|
||||
Short: "Link Gear to Activity",
|
||||
Run: gearLink,
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
gearCmd.AddCommand(gearLinkCommand)
|
||||
|
||||
gearUnlinkCommand := &cobra.Command{
|
||||
Use: "unlink <gear UUID> <activity id>",
|
||||
Short: "Unlink Gear to Activity",
|
||||
Run: gearUnlink,
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
gearCmd.AddCommand(gearUnlinkCommand)
|
||||
|
||||
gearForActivityCommand := &cobra.Command{
|
||||
Use: "activity <activity id>",
|
||||
Short: "Get Gear for Activity",
|
||||
Run: gearForActivity,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
gearCmd.AddCommand(gearForActivityCommand)
|
||||
}
|
||||
|
||||
func gearList(_ *cobra.Command, args []string) {
|
||||
var profileID int64 = 0
|
||||
var err error
|
||||
if len(args) == 1 {
|
||||
profileID, err = strconv.ParseInt(args[0], 10, 64)
|
||||
bail(err)
|
||||
}
|
||||
gear, err := client.Gear(profileID)
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("UUID", "Type", "Brand & Model", "Nickname", "Created Date", "Total Distance", "Activities")
|
||||
for _, g := range gear {
|
||||
|
||||
gearStats, err := client.GearStats(g.Uuid)
|
||||
bail(err)
|
||||
|
||||
t.AddRow(
|
||||
g.Uuid,
|
||||
g.GearTypeName,
|
||||
g.CustomMakeModel,
|
||||
g.DisplayName,
|
||||
g.CreateDate.Time,
|
||||
strconv.FormatFloat(gearStats.TotalDistance, 'f', 2, 64),
|
||||
gearStats.TotalActivities,
|
||||
)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func gearTypeList(_ *cobra.Command, _ []string) {
|
||||
gearTypes, err := client.GearType()
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Name", "Created Date", "Update Date")
|
||||
sort.Slice(gearTypes, func(i, j int) bool {
|
||||
return gearTypes[i].TypeID < gearTypes[j].TypeID
|
||||
})
|
||||
|
||||
for _, g := range gearTypes {
|
||||
t.AddRow(
|
||||
g.TypeID,
|
||||
g.TypeName,
|
||||
g.CreateDate,
|
||||
g.UpdateDate,
|
||||
)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func gearLink(_ *cobra.Command, args []string) {
|
||||
uuid := args[0]
|
||||
activityID, err := strconv.Atoi(args[1])
|
||||
bail(err)
|
||||
|
||||
err = client.GearLink(uuid, activityID)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func gearUnlink(_ *cobra.Command, args []string) {
|
||||
uuid := args[0]
|
||||
activityID, err := strconv.Atoi(args[1])
|
||||
bail(err)
|
||||
|
||||
err = client.GearUnlink(uuid, activityID)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func gearForActivity(_ *cobra.Command, args []string) {
|
||||
activityID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
gear, err := client.GearForActivity(0, activityID)
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("UUID", "Type", "Brand & Model", "Nickname", "Created Date", "Total Distance", "Activities")
|
||||
for _, g := range gear {
|
||||
|
||||
gearStats, err := client.GearStats(g.Uuid)
|
||||
bail(err)
|
||||
|
||||
t.AddRow(
|
||||
g.Uuid,
|
||||
g.GearTypeName,
|
||||
g.CustomMakeModel,
|
||||
g.DisplayName,
|
||||
g.CreateDate.Time,
|
||||
strconv.FormatFloat(gearStats.TotalDistance, 'f', 2, 64),
|
||||
gearStats.TotalActivities,
|
||||
)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
46
python-garmin-connect/connect/get.go
Normal file
46
python-garmin-connect/connect/get.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
formatJSON bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
getCmd := &cobra.Command{
|
||||
Use: "get <URL>",
|
||||
Short: "Get data from Garmin Connect, print to stdout",
|
||||
Run: get,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
getCmd.Flags().BoolVarP(&formatJSON, "json", "j", false, "Format output as indented JSON")
|
||||
rootCmd.AddCommand(getCmd)
|
||||
}
|
||||
|
||||
func get(_ *cobra.Command, args []string) {
|
||||
url := args[0]
|
||||
|
||||
if formatJSON {
|
||||
raw := bytes.NewBuffer(nil)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
|
||||
err := client.Download(url, raw)
|
||||
bail(err)
|
||||
|
||||
err = json.Indent(buffer, raw.Bytes(), "", " ")
|
||||
bail(err)
|
||||
|
||||
_, err = io.Copy(os.Stdout, buffer)
|
||||
bail(err)
|
||||
} else {
|
||||
err := client.Download(url, os.Stdout)
|
||||
bail(err)
|
||||
}
|
||||
}
|
||||
67
python-garmin-connect/connect/goals.go
Normal file
67
python-garmin-connect/connect/goals.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
goalsCmd := &cobra.Command{
|
||||
Use: "goals",
|
||||
}
|
||||
rootCmd.AddCommand(goalsCmd)
|
||||
|
||||
goalsListCmd := &cobra.Command{
|
||||
Use: "list [display name]",
|
||||
Short: "List all goals",
|
||||
Run: goalsList,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
goalsCmd.AddCommand(goalsListCmd)
|
||||
|
||||
goalsDeleteCmd := &cobra.Command{
|
||||
Use: "delete <goal id>",
|
||||
Short: "Delete a goal",
|
||||
Run: goalsDelete,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
goalsCmd.AddCommand(goalsDeleteCmd)
|
||||
}
|
||||
|
||||
func goalsList(_ *cobra.Command, args []string) {
|
||||
displayName := ""
|
||||
if len(args) == 1 {
|
||||
displayName = args[0]
|
||||
}
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Profile", "Category", "Type", "Start", "End", "Created", "Value")
|
||||
for typ := 0; typ <= 9; typ++ {
|
||||
goals, err := client.Goals(displayName, typ)
|
||||
bail(err)
|
||||
|
||||
for _, g := range goals {
|
||||
t.AddRow(
|
||||
g.ID,
|
||||
g.ProfileID,
|
||||
g.GoalCategory,
|
||||
g.GoalType,
|
||||
g.Start,
|
||||
g.End,
|
||||
g.Created,
|
||||
g.Value,
|
||||
)
|
||||
}
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func goalsDelete(_ *cobra.Command, args []string) {
|
||||
goalID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.DeleteGoal("", goalID)
|
||||
bail(err)
|
||||
}
|
||||
189
python-garmin-connect/connect/groups.go
Normal file
189
python-garmin-connect/connect/groups.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
groupsCmd := &cobra.Command{
|
||||
Use: "groups",
|
||||
}
|
||||
rootCmd.AddCommand(groupsCmd)
|
||||
|
||||
groupsListCmd := &cobra.Command{
|
||||
Use: "list [display name]",
|
||||
Short: "List all groups",
|
||||
Run: groupsList,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
groupsCmd.AddCommand(groupsListCmd)
|
||||
|
||||
groupsViewCmd := &cobra.Command{
|
||||
Use: "view <group id>",
|
||||
Short: "View group details",
|
||||
Run: groupsView,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
groupsCmd.AddCommand(groupsViewCmd)
|
||||
|
||||
groupsViewAnnouncementCmd := &cobra.Command{
|
||||
Use: "announcement <group id>",
|
||||
Short: "View group abbouncement",
|
||||
Run: groupsViewAnnouncement,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
groupsViewCmd.AddCommand(groupsViewAnnouncementCmd)
|
||||
|
||||
groupsViewMembersCmd := &cobra.Command{
|
||||
Use: "members <group id>",
|
||||
Short: "View group members",
|
||||
Run: groupsViewMembers,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
groupsViewCmd.AddCommand(groupsViewMembersCmd)
|
||||
|
||||
groupsSearchCmd := &cobra.Command{
|
||||
Use: "search <keyword>",
|
||||
Short: "Search for a group",
|
||||
Run: groupsSearch,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
groupsCmd.AddCommand(groupsSearchCmd)
|
||||
|
||||
groupsJoinCmd := &cobra.Command{
|
||||
Use: "join <group id>",
|
||||
Short: "Join a group",
|
||||
Run: groupsJoin,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
groupsCmd.AddCommand(groupsJoinCmd)
|
||||
|
||||
groupsLeaveCmd := &cobra.Command{
|
||||
Use: "leave <group id>",
|
||||
Short: "Leave a group",
|
||||
Run: groupsLeave,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
groupsCmd.AddCommand(groupsLeaveCmd)
|
||||
}
|
||||
|
||||
func groupsList(_ *cobra.Command, args []string) {
|
||||
displayName := ""
|
||||
if len(args) == 1 {
|
||||
displayName = args[0]
|
||||
}
|
||||
|
||||
groups, err := client.Groups(displayName)
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Name", "Description", "Profile Image")
|
||||
for _, g := range groups {
|
||||
t.AddRow(g.ID, g.Name, g.Description, g.ProfileImageURLLarge)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func groupsSearch(_ *cobra.Command, args []string) {
|
||||
keyword := args[0]
|
||||
groups, err := client.SearchGroups(keyword)
|
||||
bail(err)
|
||||
|
||||
lastID := 0
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("ID", "Name", "Description", "Profile Image")
|
||||
for _, g := range groups {
|
||||
if g.ID == lastID {
|
||||
continue
|
||||
}
|
||||
|
||||
t.AddRow(g.ID, g.Name, g.Description, g.ProfileImageURLLarge)
|
||||
|
||||
lastID = g.ID
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func groupsView(_ *cobra.Command, args []string) {
|
||||
id, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
group, err := client.Group(id)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("ID", group.ID)
|
||||
t.AddValue("Name", group.Name)
|
||||
t.AddValue("Description", group.Description)
|
||||
t.AddValue("OwnerID", group.OwnerID)
|
||||
t.AddValue("ProfileImageURLLarge", group.ProfileImageURLLarge)
|
||||
t.AddValue("ProfileImageURLMedium", group.ProfileImageURLMedium)
|
||||
t.AddValue("ProfileImageURLSmall", group.ProfileImageURLSmall)
|
||||
t.AddValue("Visibility", group.Visibility)
|
||||
t.AddValue("Privacy", group.Privacy)
|
||||
t.AddValue("Location", group.Location)
|
||||
t.AddValue("WebsiteURL", group.WebsiteURL)
|
||||
t.AddValue("FacebookURL", group.FacebookURL)
|
||||
t.AddValue("TwitterURL", group.TwitterURL)
|
||||
// t.AddValue("PrimaryActivities", group.PrimaryActivities)
|
||||
t.AddValue("OtherPrimaryActivity", group.OtherPrimaryActivity)
|
||||
// t.AddValue("LeaderboardTypes", group.LeaderboardTypes)
|
||||
// t.AddValue("FeatureTypes", group.FeatureTypes)
|
||||
t.AddValue("CorporateWellness", group.CorporateWellness)
|
||||
// t.AddValue("ActivityFeedTypes", group.ActivityFeedTypes)
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func groupsViewAnnouncement(_ *cobra.Command, args []string) {
|
||||
id, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
announcement, err := client.GroupAnnouncement(id)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("ID", announcement.ID)
|
||||
t.AddValue("GroupID", announcement.GroupID)
|
||||
t.AddValue("Title", announcement.Title)
|
||||
t.AddValue("ExpireDate", announcement.ExpireDate.String())
|
||||
t.AddValue("AnnouncementDate", announcement.AnnouncementDate.String())
|
||||
t.Output(os.Stdout)
|
||||
fmt.Fprintf(os.Stdout, "\n%s\n", strings.TrimSpace(announcement.Message))
|
||||
}
|
||||
|
||||
func groupsViewMembers(_ *cobra.Command, args []string) {
|
||||
id, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
members, err := client.GroupMembers(id)
|
||||
bail(err)
|
||||
|
||||
t := NewTable()
|
||||
t.AddHeader("Display Name", "Joined", "Name", "Location", "Role", "Profile Image")
|
||||
for _, m := range members {
|
||||
t.AddRow(m.DisplayName, m.Joined, m.Fullname, m.Location, m.Role, m.ProfileImageURLMedium)
|
||||
}
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func groupsJoin(_ *cobra.Command, args []string) {
|
||||
groupID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.JoinGroup(groupID)
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func groupsLeave(_ *cobra.Command, args []string) {
|
||||
groupID, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.LeaveGroup(groupID)
|
||||
bail(err)
|
||||
}
|
||||
96
python-garmin-connect/connect/info.go
Normal file
96
python-garmin-connect/connect/info.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
connect "github.com/abrander/garmin-connect"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
infoCmd := &cobra.Command{
|
||||
Use: "info [display name]",
|
||||
Short: "Show various information and statistics about a Connect User",
|
||||
Run: info,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
rootCmd.AddCommand(infoCmd)
|
||||
}
|
||||
|
||||
func info(_ *cobra.Command, args []string) {
|
||||
displayName := ""
|
||||
if len(args) == 1 {
|
||||
displayName = args[0]
|
||||
}
|
||||
|
||||
t := NewTabular()
|
||||
|
||||
socialProfile, err := client.SocialProfile(displayName)
|
||||
if err == connect.ErrNotFound {
|
||||
bail(err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
displayName = socialProfile.DisplayName
|
||||
} else {
|
||||
socialProfile, err = client.PublicSocialProfile(displayName)
|
||||
bail(err)
|
||||
|
||||
displayName = socialProfile.DisplayName
|
||||
}
|
||||
|
||||
t.AddValue("ID", socialProfile.ID)
|
||||
t.AddValue("Profile ID", socialProfile.ProfileID)
|
||||
t.AddValue("Display Name", socialProfile.DisplayName)
|
||||
t.AddValue("Name", socialProfile.Fullname)
|
||||
t.AddValue("Level", socialProfile.UserLevel)
|
||||
t.AddValue("Points", socialProfile.UserPoint)
|
||||
t.AddValue("Profile Image", socialProfile.ProfileImageURLLarge)
|
||||
|
||||
info, err := client.PersonalInformation(displayName)
|
||||
if err == nil {
|
||||
t.AddValue("", "")
|
||||
t.AddValue("Gender", info.UserInfo.Gender)
|
||||
t.AddValueUnit("Age", info.UserInfo.Age, "years")
|
||||
t.AddValueUnit("Height", nzf(info.BiometricProfile.Height), "cm")
|
||||
t.AddValueUnit("Weight", nzf(info.BiometricProfile.Weight/1000.0), "kg")
|
||||
t.AddValueUnit("Vo² Max", nzf(info.BiometricProfile.VO2Max), "mL/kg/min")
|
||||
t.AddValueUnit("Vo² Max (cycling)", nzf(info.BiometricProfile.VO2MaxCycling), "mL/kg/min")
|
||||
}
|
||||
|
||||
life, err := client.LifetimeActivities(displayName)
|
||||
if err == nil {
|
||||
t.AddValue("", "")
|
||||
t.AddValue("Activities", life.Activities)
|
||||
t.AddValueUnit("Distance", life.Distance/1000.0, "km")
|
||||
t.AddValueUnit("Time", (time.Duration(life.Duration) * time.Second).Round(time.Second).String(), "hms")
|
||||
t.AddValueUnit("Calories", life.Calories/4.184, "Kcal")
|
||||
t.AddValueUnit("Elev Gain", life.ElevationGain, "m")
|
||||
}
|
||||
|
||||
totals, err := client.LifetimeTotals(displayName)
|
||||
if err == nil {
|
||||
t.AddValue("", "")
|
||||
t.AddValueUnit("Steps", totals.Steps, "steps")
|
||||
t.AddValueUnit("Distance", totals.Distance/1000.0, "km")
|
||||
t.AddValueUnit("Daily Goal Met", totals.GoalsMetInDays, "days")
|
||||
t.AddValueUnit("Active Days", totals.ActiveDays, "days")
|
||||
if totals.ActiveDays > 0 {
|
||||
t.AddValueUnit("Average Steps", totals.Steps/totals.ActiveDays, "steps")
|
||||
}
|
||||
t.AddValueUnit("Calories", totals.Calories, "kCal")
|
||||
}
|
||||
|
||||
lastUsed, err := client.LastUsed(displayName)
|
||||
if err == nil {
|
||||
t.AddValue("", "")
|
||||
t.AddValue("Device ID", lastUsed.DeviceID)
|
||||
t.AddValue("Device", lastUsed.DeviceName)
|
||||
t.AddValue("Time", lastUsed.DeviceUploadTime.String())
|
||||
t.AddValue("Ago", time.Since(lastUsed.DeviceUploadTime.Time).Round(time.Second).String())
|
||||
t.AddValue("Image", lastUsed.ImageURL)
|
||||
}
|
||||
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
96
python-garmin-connect/connect/main.go
Normal file
96
python-garmin-connect/connect/main.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
connect "github.com/abrander/garmin-connect"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: os.Args[0] + " [command]",
|
||||
Short: "CLI Client for Garmin Connect",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
loadState()
|
||||
if verbose {
|
||||
logger := log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile)
|
||||
client.SetOptions(connect.DebugLogger(logger))
|
||||
}
|
||||
|
||||
if dumpFile != "" {
|
||||
w, err := os.OpenFile(dumpFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||
bail(err)
|
||||
client.SetOptions(connect.DumpWriter(w))
|
||||
}
|
||||
},
|
||||
PersistentPostRun: func(_ *cobra.Command, _ []string) {
|
||||
storeState()
|
||||
},
|
||||
}
|
||||
|
||||
verbose bool
|
||||
dumpFile string
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose debug output")
|
||||
rootCmd.PersistentFlags().StringVarP(&dumpFile, "dump", "d", "", "File to dump requests and responses to")
|
||||
|
||||
authenticateCmd := &cobra.Command{
|
||||
Use: "authenticate [email]",
|
||||
Short: "Authenticate against the Garmin API",
|
||||
Run: authenticate,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
rootCmd.AddCommand(authenticateCmd)
|
||||
|
||||
signoutCmd := &cobra.Command{
|
||||
Use: "signout",
|
||||
Short: "Log out of the Garmin API and forget session and password",
|
||||
Run: signout,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
rootCmd.AddCommand(signoutCmd)
|
||||
}
|
||||
|
||||
func bail(err error) {
|
||||
if err != nil {
|
||||
log.Fatalf("%s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
bail(rootCmd.Execute())
|
||||
}
|
||||
|
||||
func authenticate(_ *cobra.Command, args []string) {
|
||||
var email string
|
||||
if len(args) == 1 {
|
||||
email = args[0]
|
||||
} else {
|
||||
fmt.Print("Email: ")
|
||||
fmt.Scanln(&email)
|
||||
}
|
||||
|
||||
fmt.Print("Password: ")
|
||||
|
||||
password, err := terminal.ReadPassword(syscall.Stdin)
|
||||
bail(err)
|
||||
|
||||
client.SetOptions(connect.Credentials(email, string(password)))
|
||||
err = client.Authenticate()
|
||||
bail(err)
|
||||
|
||||
fmt.Printf("\nSuccess\n")
|
||||
}
|
||||
|
||||
func signout(_ *cobra.Command, _ []string) {
|
||||
_ = client.Signout()
|
||||
client.Password = ""
|
||||
}
|
||||
16
python-garmin-connect/connect/nzf.go
Normal file
16
python-garmin-connect/connect/nzf.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// nzf is a type that will print "-" instead of 0.0 when used as a stringer.
|
||||
type nzf float64
|
||||
|
||||
func (nzf nzf) String() string {
|
||||
if nzf != 0.0 {
|
||||
return fmt.Sprintf("%.01f", nzf)
|
||||
}
|
||||
|
||||
return "-"
|
||||
}
|
||||
62
python-garmin-connect/connect/sleep.go
Normal file
62
python-garmin-connect/connect/sleep.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
connect "github.com/abrander/garmin-connect"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sleepCmd := &cobra.Command{
|
||||
Use: "sleep",
|
||||
}
|
||||
rootCmd.AddCommand(sleepCmd)
|
||||
|
||||
sleepSummaryCmd := &cobra.Command{
|
||||
Use: "summary <date> [displayName]",
|
||||
Short: "Show sleep summary for date",
|
||||
Run: sleepSummary,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
}
|
||||
sleepCmd.AddCommand(sleepSummaryCmd)
|
||||
}
|
||||
|
||||
func sleepSummary(_ *cobra.Command, args []string) {
|
||||
date, err := connect.ParseDate(args[0])
|
||||
bail(err)
|
||||
|
||||
displayName := ""
|
||||
|
||||
if len(args) > 1 {
|
||||
displayName = args[1]
|
||||
}
|
||||
|
||||
summary, _, levels, err := client.SleepData(displayName, date.Time())
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("Start", summary.StartGMT)
|
||||
t.AddValue("End", summary.EndGMT)
|
||||
t.AddValue("Sleep", hoursAndMinutes(summary.Sleep))
|
||||
t.AddValue("Nap", hoursAndMinutes(summary.Nap))
|
||||
t.AddValue("Unmeasurable", hoursAndMinutes(summary.Unmeasurable))
|
||||
t.AddValue("Deep", hoursAndMinutes(summary.Deep))
|
||||
t.AddValue("Light", hoursAndMinutes(summary.Light))
|
||||
t.AddValue("REM", hoursAndMinutes(summary.REM))
|
||||
t.AddValue("Awake", hoursAndMinutes(summary.Awake))
|
||||
t.AddValue("Confirmed", summary.Confirmed)
|
||||
t.AddValue("Confirmation Type", summary.Confirmation)
|
||||
t.AddValue("REM Data", summary.REMData)
|
||||
t.Output(os.Stdout)
|
||||
|
||||
fmt.Fprintf(os.Stdout, "\n")
|
||||
|
||||
t2 := NewTable()
|
||||
t2.AddHeader("Start", "End", "State", "Duration")
|
||||
for _, l := range levels {
|
||||
t2.AddRow(l.Start, l.End, l.State, hoursAndMinutes(l.End.Sub(l.Start.Time)))
|
||||
}
|
||||
t2.Output(os.Stdout)
|
||||
}
|
||||
57
python-garmin-connect/connect/state.go
Normal file
57
python-garmin-connect/connect/state.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
connect "github.com/abrander/garmin-connect"
|
||||
)
|
||||
|
||||
var (
|
||||
client = connect.NewClient(
|
||||
connect.AutoRenewSession(true),
|
||||
)
|
||||
|
||||
stateFile string
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(&stateFile, "state", "s", stateFilename(), "State file to use")
|
||||
}
|
||||
|
||||
func stateFilename() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not detect home directory: %s", err.Error())
|
||||
}
|
||||
|
||||
return path.Join(home, ".garmin-connect.json")
|
||||
}
|
||||
|
||||
func loadState() {
|
||||
data, err := ioutil.ReadFile(stateFile)
|
||||
if err != nil {
|
||||
log.Printf("Could not open state file: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, client)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not unmarshal state: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func storeState() {
|
||||
b, err := json.MarshalIndent(client, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not marshal state: %s", err.Error())
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(stateFile, b, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not write state file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
70
python-garmin-connect/connect/tools.go
Normal file
70
python-garmin-connect/connect/tools.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func formatDate(t time.Time) string {
|
||||
if t == (time.Time{}) {
|
||||
return "-"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%04d-%02d-%02d", t.Year(), t.Month(), t.Day())
|
||||
}
|
||||
|
||||
func stringer(value interface{}) string {
|
||||
stringer, ok := value.(fmt.Stringer)
|
||||
if ok {
|
||||
return stringer.String()
|
||||
}
|
||||
|
||||
str := ""
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
str = v
|
||||
case int, int64:
|
||||
str = fmt.Sprintf("%d", v)
|
||||
case float64:
|
||||
str = strconv.FormatFloat(v, 'f', 1, 64)
|
||||
case bool:
|
||||
if v {
|
||||
str = gotIt
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("no idea what to do about %T:%v", value, value))
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func sliceStringer(values []interface{}) []string {
|
||||
ret := make([]string, len(values))
|
||||
|
||||
for i, value := range values {
|
||||
ret[i] = stringer(value)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func hoursAndMinutes(dur time.Duration) string {
|
||||
if dur == 0 {
|
||||
return "-"
|
||||
}
|
||||
|
||||
if dur < 60*time.Minute {
|
||||
m := dur.Truncate(time.Minute)
|
||||
|
||||
return fmt.Sprintf("%dm", m/time.Minute)
|
||||
}
|
||||
|
||||
h := dur.Truncate(time.Hour)
|
||||
m := (dur - h).Truncate(time.Minute)
|
||||
|
||||
h /= time.Hour
|
||||
m /= time.Minute
|
||||
|
||||
return fmt.Sprintf("%dh%dm", h, m)
|
||||
}
|
||||
224
python-garmin-connect/connect/weight.go
Normal file
224
python-garmin-connect/connect/weight.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
connect "github.com/abrander/garmin-connect"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
weightCmd := &cobra.Command{
|
||||
Use: "weight",
|
||||
}
|
||||
rootCmd.AddCommand(weightCmd)
|
||||
|
||||
weightLatestCmd := &cobra.Command{
|
||||
Use: "latest",
|
||||
Short: "Show the latest weight-in",
|
||||
Run: weightLatest,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
weightCmd.AddCommand(weightLatestCmd)
|
||||
|
||||
weightLatestWeekCmd := &cobra.Command{
|
||||
Use: "week",
|
||||
Short: "Show average weight for the latest week",
|
||||
Run: weightLatestWeek,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
weightLatestCmd.AddCommand(weightLatestWeekCmd)
|
||||
|
||||
weightAddCmd := &cobra.Command{
|
||||
Use: "add <yyyy-mm-dd> <weight in grams>",
|
||||
Short: "Add a simple weight for a specific date",
|
||||
Run: weightAdd,
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
weightCmd.AddCommand(weightAddCmd)
|
||||
|
||||
weightDeleteCmd := &cobra.Command{
|
||||
Use: "delete <yyyy-mm-dd]>",
|
||||
Short: "Delete a weight-in",
|
||||
Run: weightDelete,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
weightCmd.AddCommand(weightDeleteCmd)
|
||||
|
||||
weightDateCmd := &cobra.Command{
|
||||
Use: "date [yyyy-mm-dd]",
|
||||
Short: "Show weight for a specific date",
|
||||
Run: weightDate,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
weightCmd.AddCommand(weightDateCmd)
|
||||
|
||||
weightRangeCmd := &cobra.Command{
|
||||
Use: "range [yyyy-mm-dd] [yyyy-mm-dd]",
|
||||
Short: "Show weight for a date range",
|
||||
Run: weightRange,
|
||||
Args: cobra.ExactArgs(2),
|
||||
}
|
||||
weightCmd.AddCommand(weightRangeCmd)
|
||||
|
||||
weightGoalCmd := &cobra.Command{
|
||||
Use: "goal [displayName]",
|
||||
Short: "Show weight goal",
|
||||
Run: weightGoal,
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
}
|
||||
weightCmd.AddCommand(weightGoalCmd)
|
||||
|
||||
weightGoalSetCmd := &cobra.Command{
|
||||
Use: "set [goal in gram]",
|
||||
Short: "Set weight goal",
|
||||
Run: weightGoalSet,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
weightGoalCmd.AddCommand(weightGoalSetCmd)
|
||||
}
|
||||
|
||||
func weightLatest(_ *cobra.Command, _ []string) {
|
||||
weightin, err := client.LatestWeight(time.Now())
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("Date", weightin.Date.String())
|
||||
t.AddValueUnit("Weight", weightin.Weight/1000.0, "kg")
|
||||
t.AddValueUnit("BMI", weightin.BMI, "kg/m2")
|
||||
t.AddValueUnit("Fat", weightin.BodyFatPercentage, "%")
|
||||
t.AddValueUnit("Fat Mass", (weightin.Weight*weightin.BodyFatPercentage)/100000.0, "kg")
|
||||
t.AddValueUnit("Water", weightin.BodyWater, "%")
|
||||
t.AddValueUnit("Bone Mass", float64(weightin.BoneMass)/1000.0, "kg")
|
||||
t.AddValueUnit("Muscle Mass", float64(weightin.MuscleMass)/1000.0, "kg")
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func weightLatestWeek(_ *cobra.Command, _ []string) {
|
||||
now := time.Now()
|
||||
from := time.Now().Add(-24 * 6 * time.Hour)
|
||||
|
||||
average, _, err := client.Weightins(from, now)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("Average from", formatDate(from))
|
||||
t.AddValueUnit("Weight", average.Weight/1000.0, "kg")
|
||||
t.AddValueUnit("BMI", average.BMI, "kg/m2")
|
||||
t.AddValueUnit("Fat", average.BodyFatPercentage, "%")
|
||||
t.AddValueUnit("Fat Mass", (average.Weight*average.BodyFatPercentage)/100000.0, "kg")
|
||||
t.AddValueUnit("Water", average.BodyWater, "%")
|
||||
t.AddValueUnit("Bone Mass", float64(average.BoneMass)/1000.0, "kg")
|
||||
t.AddValueUnit("Muscle Mass", float64(average.MuscleMass)/1000.0, "kg")
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func weightAdd(_ *cobra.Command, args []string) {
|
||||
date, err := connect.ParseDate(args[0])
|
||||
bail(err)
|
||||
|
||||
weight, err := strconv.Atoi(args[1])
|
||||
bail(err)
|
||||
|
||||
err = client.AddUserWeight(date.Time(), float64(weight))
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func weightDelete(_ *cobra.Command, args []string) {
|
||||
date, err := connect.ParseDate(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.DeleteWeightin(date.Time())
|
||||
bail(err)
|
||||
}
|
||||
|
||||
func weightDate(_ *cobra.Command, args []string) {
|
||||
date, err := connect.ParseDate(args[0])
|
||||
bail(err)
|
||||
|
||||
tim, weight, err := client.WeightByDate(date.Time())
|
||||
bail(err)
|
||||
|
||||
zero := time.Time{}
|
||||
if tim.Time == zero {
|
||||
fmt.Printf("No weight ins on this date\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("Time", tim.String())
|
||||
t.AddValueUnit("Weight", weight/1000.0, "kg")
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func weightRange(_ *cobra.Command, args []string) {
|
||||
from, err := connect.ParseDate(args[0])
|
||||
bail(err)
|
||||
|
||||
to, err := connect.ParseDate(args[1])
|
||||
bail(err)
|
||||
|
||||
average, weightins, err := client.Weightins(from.Time(), to.Time())
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
|
||||
t.AddValueUnit("Weight", average.Weight/1000.0, "kg")
|
||||
t.AddValueUnit("BMI", average.BMI, "kg/m2")
|
||||
t.AddValueUnit("Fat", average.BodyFatPercentage, "%")
|
||||
t.AddValueUnit("Fat Mass", average.Weight*average.BodyFatPercentage/100000.0, "kg")
|
||||
t.AddValueUnit("Water", average.BodyWater, "%")
|
||||
t.AddValueUnit("Bone Mass", float64(average.BoneMass)/1000.0, "kg")
|
||||
t.AddValueUnit("Muscle Mass", float64(average.MuscleMass)/1000.0, "kg")
|
||||
fmt.Fprintf(os.Stdout, " \033[1mAverage\033[0m\n")
|
||||
t.Output(os.Stdout)
|
||||
|
||||
t2 := NewTable()
|
||||
t2.AddHeader("Date", "Weight", "BMI", "Fat%", "Fat", "Water%", "Bone Mass", "Muscle Mass")
|
||||
for _, weightin := range weightins {
|
||||
if weightin.Weight < 1.0 {
|
||||
continue
|
||||
}
|
||||
|
||||
t2.AddRow(
|
||||
weightin.Date,
|
||||
weightin.Weight/1000.0,
|
||||
nzf(weightin.BMI),
|
||||
nzf(weightin.BodyFatPercentage),
|
||||
nzf(weightin.Weight*weightin.BodyFatPercentage/100000.0),
|
||||
nzf(weightin.BodyWater),
|
||||
nzf(float64(weightin.BoneMass)/1000.0),
|
||||
nzf(float64(weightin.MuscleMass)/1000.0),
|
||||
)
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "\n")
|
||||
t2.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func weightGoal(_ *cobra.Command, args []string) {
|
||||
displayName := ""
|
||||
|
||||
if len(args) > 0 {
|
||||
displayName = args[0]
|
||||
}
|
||||
|
||||
goal, err := client.WeightGoal(displayName)
|
||||
bail(err)
|
||||
|
||||
t := NewTabular()
|
||||
t.AddValue("ID", goal.ID)
|
||||
t.AddValue("Created", goal.Created)
|
||||
t.AddValueUnit("Target", float64(goal.Value)/1000.0, "kg")
|
||||
t.Output(os.Stdout)
|
||||
}
|
||||
|
||||
func weightGoalSet(_ *cobra.Command, args []string) {
|
||||
goal, err := strconv.Atoi(args[0])
|
||||
bail(err)
|
||||
|
||||
err = client.SetWeightGoal(goal)
|
||||
bail(err)
|
||||
}
|
||||
Reference in New Issue
Block a user