Skip to content

Instantly share code, notes, and snippets.

@rcarlsen
Created April 1, 2014 15:14
Show Gist options
  • Save rcarlsen/9916212 to your computer and use it in GitHub Desktop.
Save rcarlsen/9916212 to your computer and use it in GitHub Desktop.
Fluxtream BodyTrack HR status
#!/usr/bin/python
import time
import httplib
import base64
import math
import json
# this script gets the latest HR data (within the past 5 minutes)
# from fluxtream's body track API.
# replace the below strings with your credentials
USERNAME = 'username'
PASSWORD = 'password'
# /api/guest
# # curl -u test:testtest http://fluxtream.org/api/guest
# {
# "username":"test",
# "firstname":"test",
# "lastname":"test",
# "email":"[email protected]",
# "roles":"[role_user]",
# "id":1
# }
#
# /api/bodytrack/tiles/uid/devicenickname.channelname/level.offset.json
# curl -u test:testtest http://flxtest.bodytrack.org/api/bodytrack/tiles/1/polarstrap.beatspacing/5.82564.json
# returns
# {
# "fields":["time","mean","stddev","count"],
# "data":[
# [1352729398.800067,0.966,1.485415458679199,15],
# [1352729423.768154,0.6116538461538461,0.04332740977406502,52],
# ...
# [1352743298.36375,0.51925,0.1960801780223846,8],
# [1352744129.181875,-1e+308,0,0]
# ],
# "level":5,
# "offset":82564,
# "sample_width":32,
# "type":"value"
# }
# for example, at level 5 each bin is 2^5 or 32 seconds. there are 512 bins per tile, so duration = 32*512=16384 seconds.
# the offset of the tile containing the t = 1384776000 is offset = 1384776000/16384, rounded down to the nearest integer, which is 84520.
def main():
connection = httplib.HTTPConnection('fluxtream.org')
auth = base64.encodestring(USERNAME+":"+PASSWORD).decode("ascii")
headers = {'Authorization': 'Basic %s' % auth}
user_id = login(connection, headers)
recent_hr = hr_data(connection, headers, user_id)
recent_hrv_stddev = hrv_data(connection, headers, user_id)
connection.close()
display_response = ['n/a', 'n/a']
#print('recent heart rate: ' + str(recent_hr))
# return the HR value (if acceptable), 'N/A' otherwise
if recent_hr > 0 and recent_hr < 230:
display_response[0] = str(int(recent_hr)) + ' bpm'
if recent_hrv_stddev > 0:
display_response[1] = "{:.1f}".format(recent_hrv_stddev) + ' ms'
return "\n".join(display_response)
def login(connection, headers):
url = '/api/guest'
connection.request('GET', url, headers=headers)
response = connection.getresponse()
if response.status != 200:
return -1
response_data = json.loads(response.read())
#print('login response: ' + str(response_data))
user_id = response_data['id']
return user_id
def hr_data(connection, headers, user_id):
# user_id, level.offset
level = -1 # bin_width is 2^level seconds wide (2^5 = 32 seconds, 2^-1 = 0.5 seconds)
tile_width = 2**level * 512
desired_time = time.time()
offset = int(math.floor(desired_time / tile_width))
last_hr = -1
# try to load (up to) two tiles (sometimes the "current" tile is empty)
# this means that we could be showing data potentially 2*tile_width seconds out-of-date
for i in range(2):
url = '/api/bodytrack/tiles/%s/PolarStrap.HeartRate/%s.%s.json' % (user_id, level, offset-i)
#print('hr url: ' +url)
connection.request('GET', url, headers=headers)
response = connection.getresponse()
if response.status != 200:
return -1
response_data = json.loads(response.read())
#print('hr response: ' + str(response_data))
mean_index = response_data['fields'].index('mean')
if mean_index < 0:
return -1
for sample in reversed(response_data['data']):
hr_value = sample[mean_index]
if hr_value > 0 and hr_value < 230:
last_hr = hr_value
break
# if we have found a valid hr sample then stop any additional requests
if last_hr > 0:
break
return last_hr
def hrv_data(connection, headers, user_id):
# user_id, level.offset
level = 8 # bin_width is 2^level seconds wide (2^5 = 32 seconds, 2^-1 = 0.5 seconds)
tile_width = 2**level * 512
desired_time = time.time()
offset = int(math.floor(desired_time / tile_width))
last_hrv = -1
# try to load (up to) two tiles (sometimes the "current" tile is empty)
# this means that we could be showing data potentially 2*tile_width seconds out-of-date
for i in range(2):
url = '/api/bodytrack/tiles/%s/PolarStrap.BeatSpacing/%s.%s.json' % (user_id, level, offset-i)
#print('hr url: ' +url)
connection.request('GET', url, headers=headers)
response = connection.getresponse()
if response.status != 200:
return -1
response_data = json.loads(response.read())
#print('hr response: ' + str(response_data))
stddev_index = response_data['fields'].index('stddev')
if stddev_index < 0:
return -1
# lop off the tail, which is usually an incomplete|empty bin.
for sample in reversed(response_data['data'][:-2]):
hrv = sample[stddev_index]
if hrv > 0:
last_hrv = hrv
break
# if we have found a sample then stop any additional requests
if last_hrv > 0:
break
return last_hrv * 1000 # unit seem to be seconds...want milliseconds
if __name__ == '__main__':
result = main()
print result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment