Skip to content

Commit

Permalink
Upgrade sleep to use Fitbit v1.2 API
Browse files Browse the repository at this point in the history
- Fix pre-midnight sleeps being shifted forwards a day, in the new API
  the point includes the full date and time, avoiding the confusion.
- Fix stages being uncategoried, the old code checked for 1/2/3 as ints
  when they were actually strings, the new API includes more types, and
  uses strings.
- I think the new API is also more granular (down to 30s) but less dense
  (it returns one point per stage, and tells you how long it lasted).

Old: https://dev.fitbit.com/build/reference/web-api/sleep-v1
New: https://dev.fitbit.com/build/reference/web-api/sleep

Workaround to enable it:
orcasgit/python-fitbit#128

TODO: switch to the new GFit sleep segment API:
https://developers.google.com/android/reference/com/google/android/gms/fitness/data/SleepStages
  • Loading branch information
dwoffinden committed Mar 14, 2021
1 parent 2bce68a commit fbfe209
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 17 deletions.
20 changes: 12 additions & 8 deletions convertors.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class Convertor:
# Unit conversion constants
POUNDS_PER_KILOGRAM = 2.20462
METERS_PER_MILE = 1609.34
NANOS_PER_MINUTE=1000*1000*1000*60
NANOS_PER_SECOND = 1000*1000*1000
NANOS_PER_MINUTE = NANOS_PER_SECOND*60

def __init__(self, googleCredsFile, googleDeveloperProjectNumber, tzinfo, weighTime):
""" Intialize a convertor object.
Expand Down Expand Up @@ -200,23 +201,26 @@ def ConvertFibitSleepPoint(self, date, data_point):
date -- date to which the data_point belongs to in "yyyy-mm-dd" format
data_point -- a single Fitbit intraday step data point
"""
timestamp = "{} {}".format(date, data_point['dateTime'])
timestamp = data_point['dateTime']
epoch_time_nanos = self.nano(self.EpochOfFitbitTimestamp(timestamp))

# Convert sleep data point to google fit sleep types
if data_point['value'] == 1:
sleepType = 72
elif data_point['value'] == 2:
level = data_point['level']
if level == 'light':
sleepType = 109
elif data_point['value'] == 3:
elif level == 'deep':
sleepType = 110
elif level == 'rem':
sleepType = 111
elif level == 'wake':
sleepType = 112
else:
sleepType = 72
raise AssertionError(f'unrecognised value for point {data_point}')

return dict(
dataTypeName='com.google.activity.segment',
startTimeNanos=epoch_time_nanos,
endTimeNanos=epoch_time_nanos + self.NANOS_PER_MINUTE,
endTimeNanos=epoch_time_nanos + (data_point['seconds'] * self.NANOS_PER_SECOND),
value=[dict(intVal=sleepType)]
)

Expand Down
6 changes: 5 additions & 1 deletion helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ def __init__(self, fitbitCredsFile, googleCredsFile):
def GetFitbitClient(self):
"""Returns an authenticated fitbit client object"""
logging.debug("Creating Fitbit client")
credentials = json.load(open(self.fitbitCredsFile))
credentials = json.load(open(self.fitbitCredsFile))
client = fitbit.Fitbit(**credentials)

# Use v1.2 sleep API: https://github.com/orcasgit/python-fitbit/issues/128
client.API_VERSION = 1.2

logging.debug("Fitbit client created")
return client

Expand Down
10 changes: 2 additions & 8 deletions remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,17 +232,11 @@ def SyncFitbitSleepToGoogleFit(self, date_stamp):
# Iterate over each sleep log for that day
sleep_count = 0
for fit_sleep in fitbitSleeps:
minute_points = fit_sleep['minuteData']
minute_points = fit_sleep['levels']['data']
sleep_count += 1

# save first time stamp for comparison
start_time = minute_points[0]['dateTime']
# convert to date, add 1 day, convert back to string
next_date_stamp = (datetime.strptime(date_stamp, DATE_FORMAT) + timedelta(1)).strftime(DATE_FORMAT)

# convert all fitbit data points to google fit data points
googlePoints = [self.convertor.ConvertFibitPoint((date_stamp if start_time <= point['dateTime'] else \
next_date_stamp),point,'sleep') for point in minute_points]
googlePoints = [self.convertor.ConvertFibitPoint(date_stamp,point,'sleep') for point in minute_points]

# 1. Write a fit session about sleep
google_session = self.convertor.ConvertGFitSleepSession(googlePoints, fit_sleep['logId'])
Expand Down

0 comments on commit fbfe209

Please sign in to comment.