Skip to content

Instantly share code, notes, and snippets.

@ltddev
Created August 6, 2015 19:23
Show Gist options
  • Save ltddev/215736a0d8d41862b518 to your computer and use it in GitHub Desktop.
Save ltddev/215736a0d8d41862b518 to your computer and use it in GitHub Desktop.
FitbitSleepAnalyzerController.py
# coding: utf-8
'''
FitbitSleepAnalyzer.py
This script is a simple implentation of the Python Fitbit library, primarily in order to experiment
with the api because my children bought me a Fitbit bracelet for fathers day and the device is reputed
to capture sleep data based all off its accelerometer. I wear this device all day and all night and
sync it regularly. Therefore I am able to perform a better analysis of the sleep data than Fitbit dooes.
Currently Fitbit dashboard presents a monthly view of the data but beyond that it does not essentially
do anything else. I propose for this app a multi paga/View that uses some as yet conceived algorithm
to tuly analze -- as opposed to presenting only. But this is the first initial stab at such an app.
'''
class FitbitSleepAnalyzer:
def __init__(self,ownerKey,ownerSecret,resourceOwnerKey,resourceOwnerSecret):
import fitbit
# All this because of peculiar format fitbit api requires to get a secure client
self.ownerKey = ownerKey
self.ownerSecret = ownerSecret
self.resourceOwnerKey = resourceOwnerKey
self.resourceOwnerSecret =resourceOwnerSecret
# 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()
def getSleepDataDictionary(self, datetime):
import fitbit
sleepUnitsDict = self.authd_client.get_sleep(datetime)
sleepList = sleepUnitsDict['sleep']
if sleepList==[]:
# if list is empty then return just 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
def getSleepSummaryDataDictionary(self, datetime):
import fitbit
sleepUnitsDict = self.authd_client.get_sleep(datetime)
sleepSummaryList = sleepUnitsDict['summary']
print 'summary test:'+ str(sleepUnitsDict)
if sleepSummaryList==[]:
# if list is empty then return just empty dictionary.
return[]
sleepSummaryDataDict = sleepSummaryList[0]
return sleepDataDict
import ui, console,datetime
# Bring up UI and control its actions
class ViewController():
import datetime
import console
from FitbitSleepAnalyzerController import FitbitSleepAnalyzer
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')
self.graphImageView = view['graphImageView']
self.dateSelected = view['datepicker'].date
view.present('popover')
# We make the connection to secure fitbit here in the __init__ method and cache to use from
# other nethod. Some of the numbers have been mixed up in thr final public code;Should be read from a file or passed in anyway -- hardcoding is bad!
# Just shown for educational purposes.
ownerKey = '66febeae096fe9442d10dd3e92d54de2'
ownerSecret = 'b8b002ddf50dd3525f57b9a350051b97'
resourceOwnerKey = '20eb22828f652f729002ba0f855d07f3'
resourceOwnerSecret = '48eaa2e1e11ea551d1e3579c3274cccd'
date = self.dateSelected
self.fitbitimpl = FitbitSleepAnalyzer(ownerKey,ownerSecret,resourceOwnerKey,resourceOwnerSecret)
def buttonTapped(self, sender):
if sender.name=='sleepDataButton':
self.performAnalysisTask()
elif sender.name == 'deviceButton':
self.performDeviceInfoTask()
elif sender.name == 'graphDataButton':
#self.performGraphTask()
self.getSleepSummaryDataDictionary()
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.
import datetime
import console
self.dateSelected = self.datePicker.date
curDeviceInfoList = self.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']
# Trick I read on internet on how to emulate a string buffer in Python.
strList = []
strList.append('Version: ' + deviceVersion + '\n')
strList.append('Device Type: ' + deviceType + '\n')
strList.append('Device Id: ' + deviceId + '\n')
strList.append('Battery level: ' + batteryLevel + '\n')
strList.append('Last sync: ' + lastSyncTime + '\n')
strList.append('MAC Address: ' + macAddress + '\n')
deviceInfo = ''.join(strList)
self.textView.text = deviceInfo
def performAnalysisTask(self):
import datetime
import console
self.dateSelected = self.datePicker.date
date = self.dateSelected
# This is the crucial call to get the data
sleepDataDict = self.fitbitimpl.getSleepDataDictionary(date)
#print sleepDataDict
# Basic error handling.
if sleepDataDict == []:
self.errorMessageString = 'No sleep data was found for the date selected.'
self.errorMessage.text = self.errorMessageString
self.textView.text = ''
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: ' + 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('Main Sleep? ' + str(sleepDataDict['isMainSleep']) + '\n')
strList.append('Minutes Awake: ' + str(sleepDataDict['minutesAwake']) + ' minutes\n')
strList.append('Time in Bed: ' + str(sleepDataDict['timeInBed']) + ' minutes\n')
strList.append('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
def getSleepSummaryDataDictionary(self):
self.dateSelected = self.datePicker.date
date = self.dateSelected
sleepSummaryDataDict = self.fitbitimpl.getSleepSummaryDataDictionary(date)
print 'summary test:' + str(sleepSummaryDataDict)
# Generates a graph visually comparing total sleep to total awake time as reported by fitbit
def performGraphTask(self):
# In order to add an image plot to be the image of a ui.Image object a way
# suggested by omz was to generate the plot, save to file, map image to
# the file. For some reason never yet abble to load .png files only .jpg.
import matplotlib.pyplot as plt
# todo create real plot information here
plt.plot([1, 2, 34, 4])
# Save the plot
plt.savefig('tempimage.jpg')
# assign/bind the image to ui.Image object.
self.graphImageView.image = ui.Image.named('tempimage.jpg')
'''
# MPL Plot
# A simple demo of using matplotlib in Pythonista.
import console
console.clear()
print 'Generating plot... (this may take a little while)'
import numpy
import matplotlib.pyplot as plt
import math
plt.grid(True)
plt.title('matplotlib Demo')
x = numpy.linspace(0.0, 2 * math.pi)
p1 = plt.plot(x, numpy.sin(x), lw=2, c='r')
p2 = plt.plot(x, numpy.cos(x), lw=2, c='b')
plt.legend([p1[0], p2[0]], ['sin(x)', 'cos(x)'], loc=4)
plt.show()
print 'Tip: You can tap and hold the image to save it to your photo library.'import matplotlib.pyplot as
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
plt.savefig('temp.png'
'''
console.clear()
view = ViewController()
'''
BACKGROUND INFO I HAVE DERIVED FOR MYSELF
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']
'''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment