#!/usr/bin/python3 import oauth2 as oauth, time, urllib.parse, http.client, json, datetime, sys # Configurable options STARTS_ON_MONDAY = False OUTPUT_FILE = 'fitbitweek.html' # Don't change these! CLIENT_ID = '228VMN' CONSUMER_KEY = '8b059f66b01d4e76a35c813de1b32440' CONSUMER_SECRET = '4a505d5dbb03475fb17a6ecb45b4e4c3' #REQUEST_TOKEN_URL = 'https://api.fitbit.com/oauth/request_token' REQUEST_TOKEN_URL = 'https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=' + CLIENT_ID + '&scope=profile%20activity' #ACCESS_TOKEN_URL = 'https://api.fitbit.com/oauth/access_token' ACCESS_TOKEN_URL = 'https://www.fitbit.com/oauth2/token' AUTHORIZE_URL = 'https://api.fitbit.com/oauth/authorize' SERVER = 'api.fitbit.com' def getPaceClass(pace): if pace > 0: return 'paceGood' elif pace < 0: return 'paceBad' return 'paceOK' def getPaceStr(pace): if pace > 0: return "+" + str(pace) else: return str(pace) def isStartOfWeek(day): return day.weekday() == (0 if STARTS_ON_MONDAY else 6) # 6 = sunday, 0 = monday # Based on STARTS_ON_MONDAY def getDayName(index): dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] if STARTS_ON_MONDAY: return dayNames[(index + 1) % 7] else: return dayNames[index] def getJSONResult(connection, consumer, token, url): signature_method = oauth.SignatureMethod_PLAINTEXT() oauth_request = oauth.Request.from_consumer_and_token(consumer, token=token, http_url=url) oauth_request.sign_request(signature_method, consumer, token) headers = oauth_request.to_header(realm=SERVER) connection.request('GET', url, headers=headers) resp = connection.getresponse() return json.loads(resp.read()) def main(): tokenKey = None tokenSecret = None userId = None data = {} try: with open('oauthdata.txt', 'r') as f: lines = f.readlines() for l in lines: l = l.strip() e = l.find('=') if (not l.startswith('#') and e != -1): data[l[:e]] = l[e+1:] if 'tokenKey' in data: tokenKey = data['tokenKey'] if 'tokenSecret' in data: tokenSecret = data['tokenSecret'] if 'userId' in data: userId = data['userId'] except: pass if (tokenKey is None or tokenSecret is None or userId is None): print('No Fitbit authentication found.') consumer = oauth.Consumer(CONSUMER_KEY, CONSUMER_SECRET) client = oauth.Client(consumer) # Part 1: get a request token resp, content = client.request(REQUEST_TOKEN_URL, "GET") if resp['status'] != '200': raise Exception("Invalid response %s." % resp['status']) request_token = dict(urllib.parse.parse_qsl(content)) #print "Request Token:" #print " - oauth_token = %s" % request_token['oauth_token'] #print " - oauth_token_secret = %s" % request_token['oauth_token_secret'] #print # Step 2: Redirect to the provider. Since this is a CLI script we do not # redirect. In a web application you would redirect the user to the URL # below. print("Go to the following link in your browser:") print("%s?oauth_token=%s" % (AUTHORIZE_URL, request_token['oauth_token'])) print() # After the user has granted access to you, the consumer, the provider will # redirect you to whatever URL you have told them to redirect to. You can # usually define this in the oauth_callback argument as well. accepted = 'n' while accepted.lower() == 'n': accepted = input('Have you authorized me? (y/n) ') oauth_verifier = input('What is the PIN? ') # Step 3: Once the consumer has redirected the user back to the oauth_callback # URL you can request the access token the user has approved. You use the # request token to sign this request. After this is done you throw away the # request token and use the access token returned. You should store this # access token somewhere safe, like a database, for future use. token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret']) token.set_verifier(oauth_verifier) client = oauth.Client(consumer, token) resp, content = client.request(ACCESS_TOKEN_URL, "POST") access_token = dict(urllib.parse.parse_qsl(content)) tokenKey = access_token['oauth_token'] tokenSecret = access_token['oauth_token_secret'] userId = access_token['encoded_user_id'] with open('oauthdata.txt', 'w') as f: f.write('tokenKey=' + tokenKey + '\n') f.write('tokenSecret=' + tokenSecret + '\n') f.write('userId=' + userId + '\n') print('Authorized!') #print "Access Token:" #print " - oauth_token = %s" % access_token['oauth_token'] #print " - oauth_token_secret = %s" % access_token['oauth_token_secret'] #print #print "You may now access protected resources using the access tokens above." #print consumer = oauth.Consumer(key=CONSUMER_KEY, secret=CONSUMER_SECRET) token = oauth.Token(key=tokenKey, secret=tokenSecret) #client = oauth.Client(consumer, token) url = "https://api.fitbit.com/1/user/-/activities/steps/date/today/1y.json" connection = http.client.HTTPSConnection(SERVER) userProfile = getJSONResult(connection, consumer, token, "https://api.fitbit.com/1/user/-/profile.json")['user'] #print userProfile name = userProfile['displayName'] #print name stepsData = getJSONResult(connection, consumer, token, "https://api.fitbit.com/1/user/-/activities/steps/date/today/1y.json")['activities-steps'] # TODO - multiple devices dateString = getJSONResult(connection, consumer, token, "https://api.fitbit.com/1/user/-/devices.json")[0]['lastSyncTime'] #print dateString lastSynced = datetime.datetime.strptime(dateString, "%Y-%m-%dT%H:%M:%S.%f") #print lastSynced pastDateToUse = lastSynced.date() #print pastDateToUse #print stepsData allStepData = {} earliestDate = pastDateToUse for stepData in stepsData: d = datetime.datetime.strptime(stepData['dateTime'], "%Y-%m-%d").date() if d < earliestDate: earliestDate = d allStepData[d] = int(stepData['value']) oneDay = datetime.timedelta(1) weekData = [] curDate = pastDateToUse - oneDay stop = isStartOfWeek(pastDateToUse) daysToCount = 0 sumSteps = 0 while not stop: if curDate in allStepData: daysToCount += 1 steps = allStepData[curDate] weekData.insert(0, steps) sumSteps += steps else: weekData.insert(0, None) stop = isStartOfWeek(curDate) curDate = curDate - oneDay sys.stdout.write(str(weekData) + '\n') pace = sumSteps - 10000*daysToCount sys.stdout.write("Pace: " + getPaceStr(pace) + '\n') # Gather previous weeks data previousPaces = [] weekValid = True while weekValid: weekSteps = 0 for i in range(7): if curDate in allStepData: weekSteps += allStepData[curDate] else: weekValid = False break curDate = curDate - oneDay if weekValid: previousPaces.append((curDate + oneDay, weekSteps - (10000 * 7))) todaysSteps = None today = datetime.datetime.now().date() if today in allStepData: todaysSteps = allStepData[today] #print previousPaces with open(OUTPUT_FILE, 'w') as f: f.write("\n") f.write("\n") f.write('\n') f.write('\n') f.write('\n') f.write('Fitbit Calendar Report\n') f.write('\n') f.write('\n') f.write('\n') f.write('\n') f.write('\n') f.write('

%s\'s Fitbit Calendar Report

\n' % name) f.write('

This week so far: %s\n' % (getPaceClass(pace), getPaceStr(pace))) if (todaysSteps is not None): f.write('Today so far: %d (last synced: %s)\n' % (todaysSteps, lastSynced.time())) f.write('

\n') f.write('\n') f.write('\n') for data in weekData: if data is not None: pace = data - 10000 f.write('\n' % (getPaceClass(pace), getPaceStr(pace))) else: f.write('\n') f.write('\n') f.write('
%s(no data)
\n') f.write('

Previous weeks:

\n') f.write('\n') for prev in previousPaces: f.write('\n' % (prev[0], getPaceClass(prev[1]), getPaceStr(prev[1]))) f.write('
Week of %s%s
\n') f.write('

See %s\'s public profile on Fitbit.

\n' % (userId, name)) f.write('

All data provided by Fitbit.com. This page generated by FitCalendar.

\n') f.write('

Fitbit is a registered trademark and service mark of Fitbit, Inc. FitCalendar is designed for use with the Fitbit platform. This product is not put out by Fitbit, and Fitbit does not service or warrant the functionality of this product.

\n') f.write('

Last updated: %s

\n' % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) f.write('\n') if (__name__ == '__main__'): main()