Skip to content

Instantly share code, notes, and snippets.

@ltddev
Created July 30, 2015 22:51
Show Gist options
  • Save ltddev/9f4e0135617f0a4ed8bc to your computer and use it in GitHub Desktop.
Save ltddev/9f4e0135617f0a4ed8bc to your computer and use it in GitHub Desktop.
fitbit-sleep
# coding: utf-8
# Controller class for sleep analyzer
import ui, console,datetime
# Bring up UI and control its actions
class ViewController():
def __init__(self):
view = ui.load_view('FitbitSleepAnalyzerView')
# Save off the address to use later elsewhere in class.
# from other class methods and handlers.
self.textView = view['textView']
self.datePicker = view['datepicker']
self.errorMessage = view['noDataLabel']
self.errorMessageString = view['noDataLabel']
imageView = view['imageView']
imageView.image = ui.Image.named('fitbit-1024-blk-onwhite.jpg')
view.present('popover')
#self.dateSelected = datetime.date(2015,7, 1) # prime date before selected
self.dateSelected = view['datepicker'].date
ownerKey = '66febeae096fe9442d10dd3e92d54de2'
ownerSecret = 'b8b002ddf50dd3525f57b9a350051b97'
resourceOwnerKey = '20eb22828f652f729002ba0f855d07f3'
resourceOwnerSecret = '48eaa2e1e11ea551d1e3579c3274cccd'
import datetime
import console
#from FitbitSleepAnalyzerController import *
#from FitbitSleepAnalyzerImpl import *
date = self.dateSelected
# for debug or trace
#console.clear()
# reduce duplication of getting and using client do it once per session.
def buttonTapped(self, sender):
if sender.name=='sleepDataButton':
self.performAnalysisTask()
elif sender.name == 'deviceButton':
self.performDeviceInfoTask()
def handleDatePicked(self, sender):
self.dateSelected = sender.date
def performDeviceInfoTask(self):
# todo get the auth client in the init of the controller to reduce duping.
ownerKey = '66febeae096fe9442d10dd3e92d54de2'
ownerSecret = 'b8b002ddf50dd3525f57b9a350051b97'
resourceOwnerKey = '20eb22828f652f729002ba0f855d07f3'
resourceOwnerSecret = '48eaa2e1e11ea551d1e3579c3274cccd'
import datetime
import console
#import FitbitSleepAnalyzer
#from FitbitSleepAnalyzerImpl import *
self.dateSelected = self.datePicker.date
fitbitimpl = FitbitSleepAnalyzer(ownerKey,ownerSecret,resourceOwnerKey,resourceOwnerSecret)
curDeviceInfoList = fitbitimpl.getCurrentDeviceInfo()
length = len(curDeviceInfoList)
if length==1 or length > 0:
myDevice = curDeviceInfoList[0]
batteryLevel = myDevice['battery']
lastSyncTime = myDevice['lastSyncTime']
macAddress = myDevice['mac']
deviceType = myDevice['type']
deviceId = myDevice['id']
deviceVersion = myDevice['deviceVersion']
strList = []
strList.append('Battery level: ' + batteryLevel + '\n')
strList.append('Last sync time: ' + lastSyncTime + '\n')
strList.append('MAC Address: ' + macAddress + '\n')
strList.append('Device Type: ' + deviceType + '\n')
strList.append('Device Id: ' + deviceId + '\n')
strList.append('Device Version: ' + deviceVersion)
deviceInfo = ''.join(strList)
self.textView.text = deviceInfo
def performAnalysisTask(self):
ownerKey = '66febeae096fe9442d10dd3e92d54de2'
ownerSecret = 'b8b002ddf50dd3525f57b9a350051b97'
resourceOwnerKey = '20eb22828f652f729002ba0f855d07f3'
resourceOwnerSecret = '48eaa2e1e11ea551d1e3579c3274cccd'
import datetime
import console
#import FitbitSleepAnalyzer
#from FitbitSleepAnalyzerImpl import *
#date = datetime.date(2015,7, 23) # Change date here or set up a parameter.
self.dateSelected = self.datePicker.date
fitbitimpl = FitbitSleepAnalyzer(ownerKey,ownerSecret,resourceOwnerKey,resourceOwnerSecret)
console.clear()
#fitbitimpl.printAllSleepData(date) temp delete
# with this instance we can pull out all important data
date = self.dateSelected
console.clear()
# This is the crucial call to get the data
sleepDataDict = fitbitimpl.getSleepDataDictionary(date)
# Basic error handling.
if sleepDataDict == []:
self.errorMessageString = 'No sleep data found.'
self.errorMessage.text = self.errorMessageString
return
else:
self.errorMessage.text =''
# continue processing since we have valid date with sleep data
# way to mimic a string buffer is to populate list of strings
# and then join those strings to create one new imutable string.
strList = []
strList.append('Date of Sleep: ' + str(sleepDataDict['dateOfSleep']) + '\n')
strList.append('Log Id: ' + str(sleepDataDict['logId']) + '\n')
strList.append('Start Time: ' + str(sleepDataDict['startTime']) + '\n')
strList.append('Minutes to Fall Asleep: ' + str(sleepDataDict['minutesToFallAsleep']) + ' minutes\n')
strList.append('Is this the Main Sleep: ' + str(sleepDataDict['isMainSleep']) + '\n')
strList.append('Total Minutes Awake: ' + str(sleepDataDict['minutesAwake']) + ' minutes\n')
strList.append('Total Time in Bed: ' + str(sleepDataDict['timeInBed']) + ' minutes\n')
strList.append('Total Minutes Asleep: ' + str(sleepDataDict['minutesAsleep']) + ' minutes\n')
strList.append('Restless Duration: ' + str(sleepDataDict['restlessDuration']) + ' minutes\n')
strList.append('Restless Count: ' + str(sleepDataDict['restlessCount']) + '\n')
strList.append('Awake Count: ' + str(sleepDataDict['awakeCount']) + '\n')
sleepInfo = ''.join(strList)
self.textView.text = sleepInfo
console.clear()
view = ViewController()
'''
BACKGROUND INFO
There is only one single sleep-related call made from an authorized client and that function signature is get_sleep(Datetime dt). This function returns a conglomerate data type of embedded lists and dictionaries.
If sleepUnitsDict is the main dictionary returned it has two keys, 'sleep' and 'summary'. sleepUnitsDict['sleep'] returns a list of one element which contains a number of dictionaries.
sleepUnitsDict['sleep']
sleepUnitsDict['summary']
The 'sleep' is a dictionary with these 17 keys:
logId
dateOfSleep
minutesToFallAsleep
awakeningsCount
minutesAwake
timeInBed
minutesAsleep
awakeDuration
efficiency
isMainSleep
startTime
restlessCount
rduration
restlessDuration
minuteData
minutesAfterWakeup
awakeCount
Each key has a single value except 'minuteData' which is a dictionary of time values presumably used by other calculations. The 'summary'is another dictionary with these 3 keys:
totalTimeInBed
totalMinutesAsleep
totalSleepRecords
Therefore once you get the two parts of returned value you can get at the data directly by using these keys.
sleepUnitsDict = authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
summaryDict = sleepUnitsDict['summary']
x = sleepList[0]
dataitem = x['one of the sleep keys']
dataitem = summaryDict['one of the 3 summary keys']
'''
#==============================================================================
class FitbitSleepAnalyzer:
def __init__(self,ownerKey,ownerSecret,resourceOwnerKey,resourceOwnerSecret):
import fitbit
self.ownerKey = ownerKey
self.ownerSecret = ownerSecret
self.resourceOwnerKey = resourceOwnerKey
self.resourceOwnerSecret =resourceOwnerSecret
self.value1 = 0
self.value2 = 0
self.value3 = 0
# Class variable which should be visible to other methods
self.authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
def getCurrentDeviceInfo(self):
# Returns potentially multiple devices, let caller determine
return self.authd_client.get_devices()
# Prints out the name-value pairs fom the minuteData name in sleep data record as the raw data has it.
def getSleepDataDictionary(self, datetime):
import fitbit
authd_client = self.authd_client
#fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
sleepUnitsDict = authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
if sleepList==[]:
# if list is empty then return empty dictionary.
return[]
# if we are here we're good to grab the first item in the list.'
sleepDataDict = sleepList[0] # only one item in this list, a dictionary.
return sleepDataDict
'''
todos:
1. change to use authn client we got in init; check that its usable if not THEN try to get a fresh one. Do this in all methods that use client. Other error handling to add.
'''
'''
FitbitSleepAnalyzerImpl.py (class)
This module is meant to utilize all of the capability of the exposed python sleep api to analyze sleep patterns perhaps above and beyond what fitbit itself does.
These are my current 4 keys:
consumer: "66febeae096fe9442d10dd3e92d54de2"
consumer secret: "b8b002ddf50dd3525f57b9a350051b97"
reource owner: resource_owner_key='20eb22828f652f729002ba0f855d07f3'
resource owner secret: resource_owner_secret='48eaa2e1e11ea551d1e3579c3274cccd')
There is only one single sleep-related call made from an authorized client and that function signature is get_sleep(Datetime dt). This function returns a conglomerate data type of embedded lists and dictionaries.
If sleepUnitsDict is the main dictionary returned it has two keys, 'sleep' and 'summary'. sleepUnitsDict['sleep'] returns a list of one element which contains a number of dictionaries.
sleepUnitsDict['sleep']
sleepUnitsDict['summary']
The 'sleep' is a dictionary with these 17 keys:
logId
dateOfSleep
minutesToFallAsleep
awakeningsCount
minutesAwake
timeInBed
minutesAsleep
awakeDuration
efficiency
isMainSleep
startTime
restlessCount
duration
restlessDuration
minuteData
minutesAfterWakeup
awakeCount
Each key has a single value except 'minuteData' which is a dictionary of time values presumably used by other calculations. The 'summary'is another dictionary with these 3 keys:
totalTimeInBed
totalMinutesAsleep
totalSleepRecords
Therefore once you get the two parts of returned value you can get at the data directly by using these keys.
sleepUnitsDict = authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
summaryDict = sleepUnitsDict['summary']
x = sleepList[0]
dataitem = x['one of the sleep keys']
dataitem = summaryDict['one of the 3 summary keys']
'''
class FitbitSleepAnalyzer:
def __init__(self,ownerKey,ownerSecret,resourceOwnerKey,resourceOwnerSecret):
import fitbit
self.ownerKey = ownerKey
self.ownerSecret = ownerSecret
self.resourceOwnerKey = resourceOwnerKey
self.resourceOwnerSecret =resourceOwnerSecret
self.value1 = 0
self.value2 = 0
self.value3 = 0
# Class variable which should be visible to other methods
self.authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
# todo add check for bad dates or no data for a given day and decide how to best handle the situation gracefully. May need to do this in a called method or, make the connection here in the init. Yes get the auth client here and save it as class variable like the others here. Then in each method check if client is null or not usable, if so get it again.
def printCurrentDeviceInfo(self):
import fitbit
authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
devicesList = authd_client.get_devices()
print 'Number of devices:', len(devicesList)
myDevice = devicesList[0] # I personally have only one device currently.
print 'Device Info'
print '==========='
# more useful means to populate data fields is to reference by name.
batteryLevel = myDevice['battery']
print 'Battery level:',batteryLevel
lastSyncTime = myDevice['lastSyncTime']
print 'Last sync time:',lastSyncTime
macAddress = myDevice['mac']
print 'MAC Address:', macAddress
deviceType = myDevice['type']
print 'Device Type:',deviceType
deviceId = myDevice['id']
print 'Device Id:', deviceId
deviceVersion = myDevice['deviceVersion']
print 'Device Version: ', deviceVersion
# looping useful in case where you don't assume any keys
# or you want to process all keys-values
for key in myDevice:
print key + ': ' + myDevice[key]
# Returns the sleep portion of result for a given datetime
def sleepData(self,datetime):
import fitbit
authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
sleepUnitsDict = authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
sleepDict = sleepList[0] #there only is one item in this list, a dictionar
return sleepDict
# Prints out the name-value pairs fom the minuteData name in sleep data record as the raw data has it.
def dumpMinuteDataList(self, datetime):
# minutesData is a list with integer indices
minutesData = self.getMinutesData(datetime)
for i in range(len(minutesData)):
print i, minutesData[i]
'''
todos:
1. change to use authn client we got in init; check that its usable if not THEN try to get a fresh one. Do this in all methods that use client. Other error handling to add.
'''
def dumpMinuteData(self, datetime):
# minutesData is a list with integer indices
minuteData = self.getMinuteData(datetime)
# I am currently unsure what the value that is associated with a dateTime
# represents. You see 1,2 or 3 for each minute. What's it mean?
'''
From the summary dictionary the value 'totalTimeInBed' is derived from the totals of 1, 2 and 3. The value 'totalMinutes Asleep' is derived from the total of type 1, that is 1:454 in example below. So thats where they compute totals though stil unsure of distinctions between types 1, 2 and 3
1. totalTimeInBed: 759
2. totalMinutesAsleep: 454
3. totalSleepRecords: 1
Current Sleep Value Totals = {1: 454, 2: 203, 3: 102}
Totals of 1, 2 and 3 = 759.
You will notice that the following dictionary values correspond to types 1, 2 and 3 totaled independently and totaled all together. It is these values which have been returned in the (my) result object.
timeInBed: 759
minutesAsleep: 454
awakeDuration: 102
restlessDuration: 203
'''
for i in range(len(minuteData)):
value = minuteData[i]['value']
if value == '1':
self.value1 = self.value1 + 1
elif value == '2':
self.value2 = self.value2 + 1
elif value == '3':
self.value3 = self.value3 + 1
print minuteData[i]['dateTime'], value
print
print 'Minute Value Totals for Given DateTime'
print '======================================'
print 'Value 1 Total = ' + str(self.value1)
print 'Value 2 Total = ' + str(self.value2)
print 'Value 3 Total = ' + str(self.value3)
# This method returns a dictionary of value to value totals
# over the entire sleep period. I am still figuring out what these
# values of 1,2 or 3 for each minute in all minutes covered.
def getSleepMinuteValuesTotals(self, datetime):
# minutesData is a list with integer indices
minuteData = self.getMinuteData(datetime)
# I am currently unsure what the value that is associated with a dateTime
# represents. You see 1,2 or 3 for each minute. What's it mean?
for i in range(len(minuteData)):
value = minuteData[i]['value']
if value == '1':
self.value1 = self.value1 + 1
elif value == '2':
self.value2 = self.value2 + 1
elif value == '3':
self.value3 = self.value3 + 1
valueTotalsDict ={1:self.value1, 2:self.value2, 3:self.value3}
return valueTotalsDict
def getMinuteData(self, datetime):
import fitbit
authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
sleepUnitsDict = authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
sleepDict = sleepList[0] # only is one item in this list.
return sleepDict['minuteData']
# Returns the summary part of result for a given datetime
def getSleepSummary(self, datetime):
import fitbit
authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
sleepDataDict = authd_client.get_sleep(datetime)
summaryDict = sleepDataDict['summary']
return summaryDict
# There are 3 summary keys and 17 sleep or 20 total keys to grab data
# from a results object returned.This method tests them by accessing them.
def testAllSleepKeyvalues(self, datetime): #todo better method name
import fitbit
authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
sleepUnitsDict = authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
summaryDict = sleepUnitsDict['summary']
sleepDataDict = sleepList[0] #only one item in this list which is a dictionary.
print 'Sleep Names and Values by Direct Reference (17)'
print '==============================================='
print '1. logId: ' + str(sleepDataDict['logId'])
print '2. dateOfSleep: ' + sleepDataDict['dateOfSleep']
print '3. minutesToFallAsleep: ' + str(sleepDataDict['minutesToFallAsleep'])
print '4. awakeningsCount: ' + str(sleepDataDict['awakeningsCount'])
print '5. minutesAwake: ' + str(sleepDataDict['minutesAwake'])
print '6. timeInBed: ' + str(sleepDataDict['timeInBed'])
print '7. minutesAsleep: ' + str(sleepDataDict['minutesAsleep'])
print '8. awakeDuration: ' + str(sleepDataDict['awakeDuration'])
print '9. efficiency: ' + str(sleepDataDict['isMainSleep'])
print '10. isMainSleep: ' + str(sleepDataDict['logId'])
print '11. startTime: ' + sleepDataDict['startTime']
print '12. restlessCount: ' + str(sleepDataDict['restlessCount'])
print '13. duration: ' + str(sleepDataDict['duration'])
print '14. restlessDuration: ' + str(sleepDataDict['restlessDuration'])
print '15. minutesAfterWakeup: ' + str(sleepDataDict['minutesAfterWakeup'])
print '16. awakeCount: ' + str(sleepDataDict['awakeCount'])
print '17. minuteData: ' + str(sleepDataDict['minuteData'])
print
print 'Summary Names and Values by Direct Reference (3)'
print '================================================'
print '1. totalTimeInBed: ' + str(summaryDict['totalTimeInBed'])
print '2. totalMinutesAsleep: ' + str(summaryDict['totalMinutesAsleep'])
print '3. totalSleepRecords: ' + str(summaryDict['totalSleepRecords'])
# Diagnostic simply prints to screen demonstrating api
def dumpSleepData(self,datetime):
import fitbit
authd_client = fitbit.Fitbit(ownerKey,ownerSecret,resource_owner_key=resourceOwnerKey,resource_owner_secret=resourceOwnerSecret)
sleepUnitsDict = authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
summaryDict = sleepUnitsDict['summary']
print 'sleepList length: ',len(sleepList)
print 'sleepList type:', type(sleepList)
print 'summaryDict length: ',len(summaryDict)
print 'summaryDict type ',type(summaryDict)
sleepDict = sleepList[0] #there only is one item in this list, a dictionary.
print
for key in sleepDict:
print key + ':' + str(sleepDict[key])
print
for key in summaryDict:
print key + ':' + str(summaryDict[key])
print
#___________________________________________________________________________
# Here we instantiate the class then invoke class methods
ownerKey = '66febeae096fe9442d10dd3e92d54de2'
ownerSecret = 'b8b002ddf50dd3525f57b9a350051b97'
resourceOwnerKey = '20eb22828f652f729002ba0f855d07f3'
resourceOwnerSecret = '48eaa2e1e11ea551d1e3579c3274cccd'
import datetime
import console
date = datetime.date(2015,7, 23) # Change date here or set up a parameter.
# instantiate class then invoke some void methods (no return types)
fitbitimpl = FitbitSleepAnalyzer(ownerKey,ownerSecret,resourceOwnerKey,resourceOwnerSecret)
console.clear()
#fitbitimpl.printAllSleepData(date)
#fitbitimpl.sleepSummary(date)
fitbitimpl.testAllSleepKeyvalues(date)
#fitbitimpl.dumpMinuteData(date)
#fitbitimpl.dumpMinuteData(date)
#These totals are significant for some purpose, just not sure yet.
sleepValuesTotal = fitbitimpl.getSleepMinuteValuesTotals(date)
print
print 'Current Sleep Value Totals = '+ str(sleepValuesTotal)
totalMinutesInBed = sleepValuesTotal[1] + sleepValuesTotal[2] + sleepValuesTotal[3]
print 'Totals of 1, 2 and 3 = ' + str(totalMinutesInBed)
#console.hide_output()
#print 'Value 1 Total = ' + str(getSleepMinuteValuesTotals())
[{"class":"View","attributes":{"name":"Fitbit Personal Sleep Analysis","background_color":"RGBA(1.000000,1.000000,1.000000,1.000000)","tint_color":"RGBA(0.000000,0.478000,1.000000,1.000000)","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{0, 0}, {536, 677}}","nodes":[{"class":"DatePicker","attributes":{"enabled":true,"flex":"","name":"datepicker","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"self.handleDatePicked","mode":1,"uuid":"5A139F13-2F5F-4365-84AA-19D6F2A4BAD3"},"frame":"{{29, 77}, {384.5, 216}}","nodes":[]},{"class":"Label","attributes":{"font_size":17,"enabled":true,"text":"Select a night from the selector below","flex":"","name":"label1","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","alignment":"left","uuid":"6249374E-5F8B-407E-8C39-E39272F16FAC"},"frame":"{{41.5, 20}, {350.5, 31.5}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"sleepDataButton","uuid":"E13208BF-1143-48F3-8545-116ACCB07FDC","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"self.buttonTapped","border_width":1,"title":"Sleep Data"},"frame":"{{192, 338}, {102.5, 35}}","nodes":[]},{"class":"ImageView","attributes":{"name":"imageView","uuid":"E271CC2C-7E1B-4EA8-8E3B-245B4DCF2DC9","enabled":true,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","flex":""},"frame":"{{421, 20}, {91, 90.5}}","nodes":[]},{"class":"TextView","attributes":{"alignment":"left","autocorrection_type":"no","font_size":17,"border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","enabled":true,"flex":"","text":"Analysis information will be printed here.","text_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","name":"textView","spellchecking_type":"no","editable":false,"uuid":"B690048C-4D4F-4D1B-BF29-BB82A97AB395"},"frame":"{{29, 393.5}, {483, 270}}","nodes":[]},{"class":"Button","attributes":{"font_size":15,"enabled":true,"flex":"","font_bold":false,"name":"deviceButton","uuid":"BF3D21D1-DCA8-4A7C-82B7-8F6282A9F83C","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","action":"self.buttonTapped","border_width":1,"title":"Device Data"},"frame":"{{72, 338}, {101.5, 35}}","nodes":[]},{"class":"Label","attributes":{"font_size":17,"enabled":true,"text":"","flex":"","name":"noDataLabel","border_color":"RGBA(0.000000,0.000000,0.000000,1.000000)","text_color":"RGBA(1.000000,0.000000,0.000000,1.000000)","alignment":"left","uuid":"6FAD2C86-68D0-47DA-A319-A020F3AF6CF8"},"frame":"{{302.5, 338}, {209.5, 32}}","nodes":[]}]}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment