Skip to content

Instantly share code, notes, and snippets.

@llun
Created September 11, 2015 03:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save llun/bb6c7c9d35634286dcb3 to your computer and use it in GitHub Desktop.
Save llun/bb6c7c9d35634286dcb3 to your computer and use it in GitHub Desktop.
A fitbit request hack
curl -H "Host: iphone-api.fitbit.com" -H "Accept-Locale: en_US" -H "Accept: */*" -H "Authorization: OAuth oauth_consumer_key="lmsg3wj8pjz532bhjhq0ymsi3w1z7gqs", oauth_nonce="174Oizg%2BRsyJSvY4vhxQVw%3D%3D", oauth_signature="WEwrW3%2FeoLCP3y%2FgCaHT0zgjerE%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1441887597", oauth_token="e74cb5c81c165804cc607be10f14c287", oauth_version="1.0"" -H "Proxy-Connection: keep-alive" -H "Accept-Language: METRIC" -H "User-Agent: FitbitMobile/2.13.2 (iPad; iOS 8.4.1; Scale/2.00)" -H "X-App-Version: 412" --data "activityTypeId=90009&details=%7B%22trackpoints%22%3A%5B%7B%22verticalAccuracy%22%3A10%2C%22interpolated%22%3Atrue%2C%22horizontalAccuracy%22%3A65%2C%22longitude%22%3A103.8869949949695%2C%22elevation%22%3A32.65422058105469%2C%22date%22%3A%222015-09-10T20%3A19%3A06.486%2B08%3A00%22%2C%22latitude%22%3A1.324899059130756%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A32.65422058105469%2C%22latitude%22%3A1.324899059130756%2C%22longitude%22%3A103.8869949949695%2C%22date%22%3A%222015-09-10T20%3A19%3A07.868%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A32.68418121337891%2C%22latitude%22%3A1.324882940880778%2C%22longitude%22%3A103.8870033242181%2C%22date%22%3A%222015-09-10T20%3A19%3A08.439%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A32.67929840087891%2C%22latitude%22%3A1.324849536695905%2C%22longitude%22%3A103.8870063823361%2C%22date%22%3A%222015-09-10T20%3A19%3A14.507%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A32.66745376586914%2C%22latitude%22%3A1.324830918204398%2C%22longitude%22%3A103.8870079021531%2C%22date%22%3A%222015-09-10T20%3A19%3A15.221%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A32.83294296264648%2C%22latitude%22%3A1.32482470852273%2C%22longitude%22%3A103.8870591261578%2C%22date%22%3A%222015-09-10T20%3A19%3A21.438%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A32.83449172973633%2C%22latitude%22%3A1.324778178446836%2C%22longitude%22%3A103.8870822982636%2C%22date%22%3A%222015-09-10T20%3A19%3A21.943%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.45975494384766%2C%22latitude%22%3A1.325326042676058%2C%22longitude%22%3A103.887058043705%2C%22date%22%3A%222015-09-10T20%3A19%3A28.110%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.4371337890625%2C%22latitude%22%3A1.325306675243956%2C%22longitude%22%3A103.8870504799223%2C%22date%22%3A%222015-09-10T20%3A19%3A28.719%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.62602996826172%2C%22latitude%22%3A1.325346543966857%2C%22longitude%22%3A103.8870670168964%2C%22date%22%3A%222015-09-10T20%3A19%3A34.767%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.23397064208984%2C%22latitude%22%3A1.324988482979158%2C%22longitude%22%3A103.8871354778889%2C%22date%22%3A%222015-09-10T20%3A19%3A41.378%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.11579132080078%2C%22latitude%22%3A1.324933344914441%2C%22longitude%22%3A103.8871448009379%2C%22date%22%3A%222015-09-10T20%3A19%3A42.142%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.02588653564453%2C%22latitude%22%3A1.325101115766631%2C%22longitude%22%3A103.8871395745845%2C%22date%22%3A%222015-09-10T20%3A19%3A48.061%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A32.99685668945312%2C%22latitude%22%3A1.325150651874383%2C%22longitude%22%3A103.8871338185231%2C%22date%22%3A%222015-09-10T20%3A19%3A48.677%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22interpolated%22%3Atrue%2C%22horizontalAccuracy%22%3A65%2C%22longitude%22%3A103.8870945661871%2C%22elevation%22%3A33.24504089355469%2C%22date%22%3A%222015-09-10T20%3A19%3A52.583%2B08%3A00%22%2C%22latitude%22%3A1.324979317126157%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.24504089355469%2C%22latitude%22%3A1.324979317126157%2C%22longitude%22%3A103.8870945661871%2C%22date%22%3A%222015-09-10T20%3A19%3A54.679%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22horizontalAccuracy%22%3A65%2C%22elevation%22%3A33.31348037719727%2C%22latitude%22%3A1.32495866779521%2C%22longitude%22%3A103.887091698979%2C%22date%22%3A%222015-09-10T20%3A19%3A55.134%2B08%3A00%22%7D%2C%7B%22verticalAccuracy%22%3A10%2C%22interpolated%22%3Atrue%2C%22horizontalAccuracy%22%3A65%2C%22longitude%22%3A103.887091698979%2C%22elevation%22%3A33.31348037719727%2C%22date%22%3A%222015-09-10T20%3A19%3A55.315%2B08%3A00%22%2C%22latitude%22%3A1.32495866779521%7D%5D%2C%22wireId%22%3A%22ab7b37edd932%22%2C%22events%22%3A%5B%7B%22type%22%3A%22start%22%2C%22time%22%3A%222015-09-10T20%3A19%3A06.486%2B08%3A00%22%7D%2C%7B%22type%22%3A%22pause%22%2C%22time%22%3A%222015-09-10T20%3A19%3A52.583%2B08%3A00%22%7D%2C%7B%22type%22%3A%22stop%22%2C%22time%22%3A%222015-09-10T20%3A19%3A55.315%2B08%3A00%22%7D%5D%7D&duration=46097&manualDistance=0.1583938147440627&startTime=2015-09-10T20%3A19%3A06.486" --compressed https://iphone-api.fitbit.com/1.1/user/-/activities.json
var fs = require('fs')
, querystring = require('querystring')
, builder = require('xmlbuilder')
, moment = require('moment')
, geolib = require('geolib')
, _ = require('lodash')
var heartRates = JSON.parse(fs.readFileSync('./330036086.json', { encoding: 'utf-8' }))
var activity = JSON.parse(fs.readFileSync('./log.json', { encoding: 'utf-8' }))
, details = JSON.parse(activity.details)
var start = details.trackpoints[0]
, stop = details.trackpoints[details.trackpoints.length - 1]
// console.log ((Date.parse(stop.date) - Date.parse(start.date)) / 1000)
// console.log (Date.parse(start.date))
var series = {}, hr = {}, firstHR = 0, lastHR = 0, previousPointHr = 0
heartRates.heartRateDataPoints.forEach(function(point, index) {
// console.log(moment(point.dateTime).unix())
var time = moment(point.dateTime, 'YYYY-M-D HH:mm:ss').unix()
if (index == 0) firstHR = time;
if (index == heartRates.heartRateDataPoints.length - 1) lastHR = time;
hr[time] = parseInt(point.bpm)
})
var beginSeries = moment(activity.startTime+'+08:00').unix();
var endSeries = moment(details.trackpoints[details.trackpoints.length - 1].date).unix() + 1
for (var i = beginSeries; i < endSeries; i++) {
var point = i;
if (point < firstHR) point = firstHR;
else if (point > lastHR) point = lastHR;
else if (!hr[point]) point = previousPointHr;
series[i] = hr[point];
previousPointHr = point;
}
var pattern = 'YYYY-MM-DDTHH:mm:ss.SSSZ'
// console.log (activity.startTime)
// console.log (moment(Date.parse(`${activity.startTime}+08:00`)).format(pattern))
var accumulateDistance = 0
var tracks = details.trackpoints.map(function(point, index) {
if (index > 0) {
var previousPoint = details.trackpoints[index - 1]
var previousLocation = { latitude: previousPoint.latitude, longitude: previousPoint.longitude, elevation: previousPoint.elevation }
var currentPointLocation = { latitude: point.latitude, longitude: point.longitude, elevation: point.elevation }
var distance = geolib.getDistance(previousLocation, currentPointLocation, 1)
// console.log (previousLocation, currentPointLocation, distance)
accumulateDistance += distance
}
return {
Trackpoint: {
Time: point.date,
Position: {
LatitudeDegrees: point.latitude,
LongitudeDegrees: point.longitude,
},
AltitudeMeters: point.elevation,
DistanceMeters: accumulateDistance,
HeartRateBpm: {
Value: series[moment(point.date).unix()]
}
}
}
})
var xml = builder.create({ TrainingCenterDatabase: {
'@xmlns': 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2',
Activities: {
Activity: {
'@Sport': 'Biking',
Id: activity.startTime + '+08:00',
Lap: {
'@StartTime': activity.startTime + '+08:00',
TotalTimeSeconds: Math.floor(activity.duration / 1000),
DistanceMeters: accumulateDistance,
Calories: heartRates.calories,
Intensity: 'Active',
TriggerMethod: 'Manual',
Track: {
'#list': tracks
}
},
Creator: {
'@xsi:type': 'Device_t',
'@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
UnitId: 0,
ProductID: 0
}
}
}
}}).dec('1.0', 'UTF-8', true)
// console.log (xml.end({ pretty: true }))
// console.log (activity.manualDistance * 1000)
// console.log (accumulateDistance)
fs.writeFileSync('out.tcx', xml.end({pretty: true}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment