Write step count summary to Google Fit.
authorFlorian Forster <ff@octo.it>
Thu, 11 Jan 2018 20:45:43 +0000 (21:45 +0100)
committerFlorian Forster <ff@octo.it>
Thu, 11 Jan 2018 20:45:43 +0000 (21:45 +0100)
gfit/gfit.go
gfitsync.go

index 028eee0..ed8f7cb 100644 (file)
@@ -4,11 +4,14 @@ import (
        "context"
        "fmt"
        "net/http"
+       "time"
 
        "github.com/octo/gfitsync/app"
        "golang.org/x/oauth2"
        oauth2google "golang.org/x/oauth2/google"
        fitness "google.golang.org/api/fitness/v1"
+       "google.golang.org/appengine"
+       "google.golang.org/appengine/log"
 )
 
 var oauthConfig = &oauth2.Config{
@@ -24,6 +27,14 @@ var oauthConfig = &oauth2.Config{
 
 const csrfToken = "@CSRFTOKEN@"
 
+func Application(ctx context.Context) *fitness.Application {
+       return &fitness.Application{
+               Name:       "Fitbit to Google Fit sync",
+               Version:    appengine.VersionID(ctx),
+               DetailsUrl: "", // optional
+       }
+}
+
 func AuthURL() string {
        return oauthConfig.AuthCodeURL(csrfToken, oauth2.AccessTypeOffline)
 }
@@ -60,3 +71,58 @@ func NewClient(ctx context.Context, u *app.User) (*Client, error) {
                Service: service,
        }, nil
 }
+
+func (c *Client) SetSteps(ctx context.Context, steps int, date time.Time) error {
+       const userID = "me"
+       const dataTypeName = "com.google.step_count.delta"
+       dataSource := &fitness.DataSource{
+               Application:    Application(ctx),
+               DataStreamName: "", // "daily summary"?
+               DataType: &fitness.DataType{
+                       Field: []*fitness.DataTypeField{
+                               &fitness.DataTypeField{
+                                       Format: "integer",
+                                       Name:   "steps",
+                               },
+                       },
+                       Name: dataTypeName,
+               },
+               Name: "Step Count",
+               Type: "raw",
+       }
+
+       dataSource, err := c.Service.Users.DataSources.Create(userID, dataSource).Context(ctx).Do()
+       if err != nil {
+               log.Errorf(ctx, "c.Service.Users.DataSources.Create() = (%+v, %v)", dataSource, err)
+               return err
+       }
+       dataSourceID := dataSource.DataStreamId
+
+       startTimeNanos := date.UnixNano()
+       endTimeNanos := date.Add(86399999999999 * time.Nanosecond).UnixNano()
+       datasetID := fmt.Sprintf("%d-%d", startTimeNanos, endTimeNanos)
+       dataset := &fitness.Dataset{
+               MinStartTimeNs: startTimeNanos,
+               MaxEndTimeNs:   endTimeNanos,
+               Point: []*fitness.DataPoint{
+                       &fitness.DataPoint{
+                               ComputationTimeMillis: time.Now().UnixNano() / 1000000,
+                               DataTypeName:          dataTypeName,
+                               StartTimeNanos:        startTimeNanos,
+                               EndTimeNanos:          endTimeNanos,
+                               Value: []*fitness.Value{
+                                       &fitness.Value{
+                                               IntVal: int64(steps),
+                                       },
+                               },
+                       },
+               },
+       }
+
+       dataset, err = c.Service.Users.DataSources.Datasets.Patch(userID, dataSourceID, datasetID, dataset).Context(ctx).Do()
+       if err != nil {
+               log.Errorf(ctx, "c.Service.Users.DataSources.Datasets.Patch() = (%+v, %v)", dataset, err)
+               return err
+       }
+       return nil
+}
index 2bc3b73..22f4c56 100644 (file)
@@ -218,21 +218,31 @@ func handleNotification(ctx context.Context, s *fitbit.Subscription) error {
        if err != nil {
                return err
        }
-       c, err := fitbit.NewClient(ctx, s.OwnerID, u)
+
+       tm, err := time.Parse("2006-01-02", s.Date)
        if err != nil {
                return err
        }
 
-       tm, err := time.Parse("2006-01-02", s.Date)
+       fitbitClient, err := fitbit.NewClient(ctx, s.OwnerID, u)
        if err != nil {
                return err
        }
 
-       summary, err := c.ActivitySummary(tm)
+       summary, err := fitbitClient.ActivitySummary(tm)
        if err != nil {
                return err
        }
-
        log.Debugf(ctx, "ActivitySummary for %s = %+v", u.Email, summary)
+
+       gfitClient, err := gfit.NewClient(ctx, u)
+       if err != nil {
+               return err
+       }
+
+       if err := gfitClient.SetSteps(ctx, summary.Summary.Steps, tm); err != nil {
+               return fmt.Errorf("gfitClient.SetSteps(%d) = %v", summary.Summary.Steps, err)
+       }
+
        return nil
 }