X-Git-Url: https://git.octo.it/?p=kraftakt.git;a=blobdiff_plain;f=kraftakt.go;h=72389bd1430b36450a071d09d8a491597d4805d7;hp=4700104d42b8d0216fb9f202e633a92b5e5a04fb;hb=HEAD;hpb=9cd8936ef5691454d4370766f78bc1e2ce7f9e23 diff --git a/kraftakt.go b/kraftakt.go index 4700104..72389bd 100644 --- a/kraftakt.go +++ b/kraftakt.go @@ -258,7 +258,7 @@ func fitbitNotifyHandler(ctx context.Context, w http.ResponseWriter, r *http.Req // Fitbit recommendation: "If signature verification fails, you should // respond with a 404" if !fitbit.CheckSignature(ctx, data, r.Header.Get("X-Fitbit-Signature")) { - log.Warningf(ctx, "signature mismatch") + log.Errorf(ctx, "signature mismatch") w.WriteHeader(http.StatusNotFound) return nil } @@ -285,22 +285,37 @@ func handleNotifications(ctx context.Context, payload []byte) error { return err } + wg := &sync.WaitGroup{} + for _, s := range subscriptions { - if s.CollectionType != "activities" { + switch s.CollectionType { + case "activities": + wg.Add(1) + go func(s fitbit.Subscription) { + defer wg.Done() + if err := activitiesNotification(ctx, &s); err != nil { + log.Warningf(ctx, "activitiesNotification() = %v", err) + } + }(s) // copies s + case "sleep": + wg.Add(1) + go func(s fitbit.Subscription) { + defer wg.Done() + if err := sleepNotification(ctx, &s); err != nil { + log.Warningf(ctx, "sleepNotification() = %v", err) + } + }(s) // copies s + default: log.Warningf(ctx, "ignoring collection type %q", s.CollectionType) - continue - } - if err := handleNotification(ctx, &s); err != nil { - log.Errorf(ctx, "handleNotification() = %v", err) - continue } } + wg.Wait() return nil } -func handleNotification(ctx context.Context, s *fitbit.Subscription) error { +func activitiesNotification(ctx context.Context, s *fitbit.Subscription) error { u, err := fitbit.UserFromSubscriberID(ctx, s.SubscriptionID) if err != nil { return err @@ -418,7 +433,7 @@ func handleNotification(ctx context.Context, s *fitbit.Subscription) error { activities = append(activities, gfit.Activity{ Start: startTime, End: endTime, - Type: gfit.ParseFitbitActivity(a.Name), + Type: a.Name, }) } if err := gfitClient.SetActivities(ctx, activities, tm); err != nil { @@ -434,3 +449,78 @@ func handleNotification(ctx context.Context, s *fitbit.Subscription) error { } return nil } + +func sleepNotification(ctx context.Context, s *fitbit.Subscription) error { + u, err := fitbit.UserFromSubscriberID(ctx, s.SubscriptionID) + if err != nil { + return err + } + + var ( + wg = &sync.WaitGroup{} + gfitClient *gfit.Client + gfitErr error + ) + + wg.Add(1) + go func() { + gfitClient, gfitErr = gfit.NewClient(ctx, u) + wg.Done() + }() + + fitbitClient, err := fitbit.NewClient(ctx, s.OwnerID, u) + if err != nil { + return err + } + + profile, err := fitbitClient.Profile(ctx) + if err != nil { + return err + } + + tm, err := time.ParseInLocation("2006-01-02", s.Date, profile.Timezone) + if err != nil { + return err + } + + sleep, err := fitbitClient.Sleep(ctx, tm) + if err != nil { + return err + } + log.Debugf(ctx, "fitbitClient.Sleep(%v) returned %d sleep stages", tm, len(sleep.Stages)) + + var activities []gfit.Activity + for _, stg := range sleep.Stages { + a := gfit.Activity{ + Start: stg.StartTime, + End: stg.EndTime, + } + switch stg.Level { + case fitbit.SleepLevelDeep: + a.Type = "Deep sleep" + case fitbit.SleepLevelLight: + a.Type = "Light sleep" + case fitbit.SleepLevelREM: + a.Type = "REM sleep" + case fitbit.SleepLevelWake: + a.Type = "Awake (during sleep cycle)" + default: + log.Warningf(ctx, "unexpected sleep level %v", stg.Level) + continue + } + + activities = append(activities, a) + } + + wg.Wait() + if gfitErr != nil { + return gfitErr + } + + log.Debugf(ctx, "passing %d activities to gfitClient.SetActivities()", len(activities)) + if err := gfitClient.SetActivities(ctx, activities, tm); err != nil { + return fmt.Errorf("SetActivities() = %v", err) + } + + return nil +}