Created
April 1, 2014 15:14
-
-
Save rcarlsen/9916212 to your computer and use it in GitHub Desktop.
Fluxtream BodyTrack HR status
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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