-
Notifications
You must be signed in to change notification settings - Fork 4
/
sleep.go
330 lines (300 loc) · 10.4 KB
/
sleep.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
package fitbit
import (
"encoding/json"
"errors"
"fmt"
"net/url"
"strconv"
"time"
)
// SleepDay contains data of a sleep day
type SleepDay struct {
Sleep []struct {
DateOfSleep string `json:"dateOfSleep"`
Duration int `json:"duration"`
Efficiency int `json:"efficiency"`
EndTime string `json:"endTime"`
InfoCode int `json:"infoCode"`
IsMainSleep bool `json:"isMainSleep"`
Levels struct {
Data []struct {
DateTime string `json:"dateTime"`
Level string `json:"level"`
Seconds int `json:"seconds"`
} `json:"data"`
ShortData []struct {
DateTime string `json:"dateTime"`
Level string `json:"level"`
Seconds int `json:"seconds"`
} `json:"shortData"`
Summary struct {
Deep struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"deep"`
Light struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"light"`
Rem struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"rem"`
Wake struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"wake"`
Asleep struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes,omitempty"`
} `json:"asleep,omitempty"`
Awake struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes,omitempty"`
} `json:"awake,omitempty"`
Restless struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes,omitempty"`
} `json:"restless,omitempty"`
} `json:"summary"`
} `json:"levels,omitempty"`
LogID int64 `json:"logId"`
MinutesAfterWakeup int `json:"minutesAfterWakeup"`
MinutesAsleep int `json:"minutesAsleep"`
MinutesAwake int `json:"minutesAwake"`
MinutesToFallAsleep int `json:"minutesToFallAsleep"`
LogType string `json:"logType"`
StartTime string `json:"startTime"`
TimeInBed int `json:"timeInBed"`
Type string `json:"type"`
} `json:"sleep"`
Summary struct {
Stages struct {
Deep int `json:"deep"`
Light int `json:"light"`
Rem int `json:"rem"`
Wake int `json:"wake"`
} `json:"stages"`
TotalMinutesAsleep int `json:"totalMinutesAsleep"`
TotalSleepRecords int `json:"totalSleepRecords"`
TotalTimeInBed int `json:"totalTimeInBed"`
} `json:"summary,omitempty"`
Meta struct {
RetryDuration int `json:"retryDuration"`
State string `json:"state"`
} `json:"meta,omitempty"`
}
// SleepByDay returns the sleep data for a given date
// date must be in the format yyyy-MM-dd
func (m *Session) SleepByDay(day string) (SleepDay, error) {
contents, err := m.makeRequest(fmt.Sprintf("https://api.fitbit.com/1.2/user/-/sleep/date/%s.json", day))
if err != nil {
return SleepDay{}, err
}
sleep := SleepDay{}
if err := json.Unmarshal(contents, &sleep); err != nil {
return SleepDay{}, err
}
return sleep, nil
}
// SleepByDayRange returns the sleep data for a given date range
// date must be in the format yyyy-MM-dd
func (m *Session) SleepByDayRange(startDay string, endDay string) (SleepDay, error) {
contents, err := m.makeRequest(fmt.Sprintf("https://api.fitbit.com/1.2/user/-/sleep/date/%s/%s.json", startDay, endDay))
if err != nil {
return SleepDay{}, err
}
sleep := SleepDay{}
if err := json.Unmarshal(contents, &sleep); err != nil {
return SleepDay{}, err
}
return sleep, nil
}
// SleepLogList returns the sleep log list for based on given parameters
func (m *Session) SleepLogList(params LogListParameters) (SleepLogList, error) {
parameterList := url.Values{}
//nolint:gocritic
if params.BeforeDate != "" {
parameterList.Add("beforeDate", params.BeforeDate)
parameterList.Add("sort", "desc")
} else if params.AfterDate != "" {
parameterList.Add("afterDate", params.AfterDate)
parameterList.Add("sort", "asc")
} else {
return SleepLogList{}, errors.New("beforeDate or afterDate must be given")
}
if params.Limit > 0 {
if params.Limit > 20 {
return SleepLogList{}, errors.New("limit must be 20 or less")
}
parameterList.Add("limit", strconv.Itoa(params.Limit))
}
parameterList.Add("offset", strconv.Itoa(params.Offset))
contents, err := m.makeRequest("https://api.fitbit.com/1.2/user/-/sleep/list.json?" + parameterList.Encode())
if err != nil {
return SleepLogList{}, err
}
activityResponse := SleepLogList{}
if err := json.Unmarshal(contents, &activityResponse); err != nil {
return SleepLogList{}, err
}
return activityResponse, nil
}
// SleepLogList defines the structure of SleepLogList based on an response
type SleepLogList struct {
Pagination struct {
AfterDate string `json:"afterDate"`
BeforeDate string `json:"beforeDate"`
Limit int `json:"limit"`
Next string `json:"next"`
Offset int `json:"offset"`
Previous string `json:"previous"`
Sort string `json:"sort"`
} `json:"pagination"`
Sleep []struct {
AwakeCount int `json:"awakeCount,omitempty"`
AwakeDuration int `json:"awakeDuration,omitempty"`
AwakeningsCount int `json:"awakeningsCount,omitempty"`
DateOfSleep string `json:"dateOfSleep"`
Duration int `json:"duration"`
Efficiency int `json:"efficiency"`
EndTime string `json:"endTime"`
InfoCode int `json:"infoCode"`
IsMainSleep bool `json:"isMainSleep"`
Levels struct {
Data []struct {
DateTime string `json:"dateTime"`
Level string `json:"level"`
Seconds int `json:"seconds"`
} `json:"data,omitempty"`
ShortData []struct {
DateTime string `json:"dateTime"`
Level string `json:"level"`
Seconds int `json:"seconds"`
} `json:"shortData,omitempty"`
Summary struct {
Deep struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"deep,omitempty"`
Light struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"light,omitempty"`
Rem struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"rem,omitempty"`
Wake struct {
Count int `json:"count"`
Minutes int `json:"minutes"`
ThirtyDayAvgMinutes int `json:"thirtyDayAvgMinutes"`
} `json:"wake,omitempty"`
} `json:"summary,omitempty"`
} `json:"levels,omitempty"`
LogID int64 `json:"logId"`
MinutesAfterWakeup int `json:"minutesAfterWakeup"`
MinutesAsleep int `json:"minutesAsleep"`
MinutesAwake int `json:"minutesAwake"`
MinutesToFallAsleep int `json:"minutesToFallAsleep"`
RestlessCount int `json:"restlessCount,omitempty"`
RestlessDuration int `json:"restlessDuration,omitempty"`
LogType string `json:"logType"`
StartTime string `json:"startTime"`
TimeInBed int `json:"timeInBed"`
Type string `json:"type"`
MinuteData []struct {
DateTime string `json:"dateTime"`
Value string `json:"value"`
} `json:"minuteData,omitempty"`
} `json:"sleep"`
}
// AddSleep adds a new sleep record
// date in form of yyyy-MM-dd
// startTime in form of HH:mm
// duration in milliseconds
func (m *Session) AddSleep(date string, startTime string, duration int64) (SleepDay, error) {
parameterList := url.Values{}
if date != "" {
parameterList.Add("date", date)
} else {
return SleepDay{}, errors.New("date must be given")
}
if startTime != "" {
parameterList.Add("startTime", startTime)
} else {
return SleepDay{}, errors.New("startTime must be given")
}
if duration > 0 {
parameterList.Add("duration", strconv.FormatInt(duration, 10))
}
contents, err := m.makeRequest("https://api.fitbit.com/1.2/user/-/sleep.json?" + parameterList.Encode())
if err != nil {
return SleepDay{}, err
}
activityResponse := SleepDay{}
if err := json.Unmarshal(contents, &activityResponse); err != nil {
return SleepDay{}, err
}
return activityResponse, nil
}
// RemoveSleep removes a sleep entry
func (m *Session) RemoveSleep(sleepID uint64) error {
_, err := m.makeDELETERequest(fmt.Sprintf("https://api.fitbit.com/1.2/user/-/sleep/%d.json", sleepID))
if err != nil {
return err
}
return nil
}
// SleepGoal requests the sleep goal of the user
func (m *Session) SleepGoal() (SleepGoal, error) {
contents, err := m.makeRequest("https://api.fitbit.com/1.2/user/-/sleep/goal.json")
if err != nil {
return SleepGoal{}, err
}
sleepGoalResponse := SleepGoal{}
if err := json.Unmarshal(contents, &sleepGoalResponse); err != nil {
return SleepGoal{}, err
}
return sleepGoalResponse, nil
}
// SleepGoal defines the structure of SleepGoal response
type SleepGoal struct {
Consistency struct {
AwakeRestlessPercentage float64 `json:"awakeRestlessPercentage"`
FlowID int `json:"flowId"`
RecommendedSleepGoal int `json:"recommendedSleepGoal"`
TypicalDuration int `json:"typicalDuration"`
TypicalWakeupTime string `json:"typicalWakeupTime"`
} `json:"consistency"`
Goal struct {
Bedtime string `json:"bedtime"`
MinDuration int `json:"minDuration"`
UpdatedOn time.Time `json:"updatedOn"`
WakeupTime string `json:"wakeupTime"`
} `json:"goal"`
}
// SetSleepGoal requests the sleep goal of the user
func (m *Session) SetSleepGoal(minDuration int) (SleepGoal, error) {
contents, err := m.makePOSTRequest("https://api.fitbit.com/1.2/user/-/sleep/goal.json", map[string]string{
"duration": strconv.Itoa(minDuration),
})
if err != nil {
return SleepGoal{}, err
}
sleepGoalResponse := SleepGoal{}
if err := json.Unmarshal(contents, &sleepGoalResponse); err != nil {
return SleepGoal{}, err
}
return sleepGoalResponse, nil
}