More trial-and-error fixes.
[kraftakt.git] / fitbit / fitbit.go
1 package fitbit
2
3 import (
4         "context"
5         "encoding/json"
6         "fmt"
7         "net/http"
8         "time"
9
10         "golang.org/x/oauth2"
11         oauth2fitbit "golang.org/x/oauth2/fitbit"
12 )
13
14 var oauth2Config = &oauth2.Config{
15         ClientID:     "@FITBIT_CLIENT_ID@",
16         ClientSecret: "@FITBIT_CLIENT_SECRET@",
17         Endpoint:     oauth2fitbit.Endpoint,
18         RedirectURL:  "https://fitbit-gfit-sync.appspot.com/fitbit/grant",
19         Scopes:       []string{"activity"},
20 }
21
22 type Activity struct {
23         ActivityID       int     `json:"activityId"`
24         ActivityParentID int     `json:"activityParentId"`
25         Calories         int     `json:"calories"`
26         Description      string  `json:"description"`
27         Distance         float64 `json:"distance"`
28         Duration         int     `json:"duration"`
29         HasStartTime     bool    `json:"hasStartTime"`
30         IsFavorite       bool    `json:"isFavorite"`
31         LogID            int     `json:"logId"`
32         Name             string  `json:"name"`
33         StartTime        string  `json:"startTime"`
34         Steps            int     `json:"steps"`
35 }
36
37 type Distance struct {
38         Activity string  `json:"activity"`
39         Distance float64 `json:"distance"`
40 }
41
42 type ActivitySummary struct {
43         Activities []Activity `json:"activities"`
44         Goals      struct {
45                 CaloriesOut int     `json:"caloriesOut"`
46                 Distance    float64 `json:"distance"`
47                 Floors      int     `json:"floors"`
48                 Steps       int     `json:"steps"`
49         } `json:"goals"`
50         Summary struct {
51                 ActivityCalories     int        `json:"activityCalories"`
52                 CaloriesBMR          int        `json:"caloriesBMR"`
53                 CaloriesOut          int        `json:"caloriesOut"`
54                 MarginalCalories     int        `json:"marginalCalories"`
55                 Distances            []Distance `json:"distances"`
56                 Elevation            float64    `json:"elevation"`
57                 Floors               int        `json:"floors"`
58                 Steps                int        `json:"steps"`
59                 SedentaryMinutes     int        `json:"sedentaryMinutes"`
60                 LightlyActiveMinutes int        `json:"lightlyActiveMinutes"`
61                 FairlyActiveMinutes  int        `json:"fairlyActiveMinutes"`
62                 VeryActiveMinutes    int        `json:"veryActiveMinutes"`
63         } `json:"summary"`
64 }
65
66 type Client struct {
67         userID string
68         client *http.Client
69 }
70
71 func NewClient(ctx context.Context, userID string, tok *oauth2.Token) *Client {
72         return &Client{
73                 userID: userID,
74                 client: oauth2Config.Client(ctx, tok),
75         }
76 }
77
78 func (c *Client) ActivitySummary(t time.Time) (*ActivitySummary, error) {
79         url := fmt.Sprintf("https://api.fitbit.com/1/user/%s/activity/date/%s.json",
80                 c.userID, t.Format("2006-01-02"))
81
82         res, err := c.client.Get(url)
83         if err != nil {
84                 return nil, err
85         }
86         defer res.Body.Close()
87
88         var summary ActivitySummary
89         if err := json.NewDecoder(res.Body).Decode(&summary); err != nil {
90                 return nil, err
91         }
92
93         return &summary, nil
94 }