Access to Fitbit API with Scala

The developer API of Fitbit provides access to the data collected by it’s personal trackers for use with custom applications development. Besides read also write access can be used not only to it’s own but on behalf of other platform users via OAuth authentication. A comprehensive documentation of the Fitbit API can be found here: https://dev.fitbit.com/docs/ .

In an attempt to collect my own data for exploration I created this little sample project using OAuth2 to connect to my own data. For OAuth to work it needs to be registered as an application at Fitbit. Also the user needs to visit the OAuth authorization page of Fibit to authorize the application for data access.

class FitBitToken {

  var access_token:String = ""
  var refresh_token:String = ""
  var user_id:String = ""
 
  // ... client secret, key, ... info
  
  def tokenRequest = {

    println("To get token please open: nhttps://www.fitbit.com/oauth2/authorize?response_type=code&client_id=227P25&redirect_uri=http%3A%2F%2Fhenning.kropponline.de&scope=activity")
    val token = scala.io.StdIn.readLine().trim

    val client_cred:String = java.util.Base64.getEncoder
      .encodeToString(s"$app_client_id:$app_client_secret".getBytes)


    val response: HttpResponse[String] = Http("https://api.fitbit.com/oauth2/token")
      .headers(Seq("Authorization" -> s"Basic $client_cred",
                   "Content-Type" -> "application/x-www-form-urlencoded"))
      .postForm(Seq("name" -> "jon",
                    "client_id" -> app_client_id,
                    "grant_type" -> "authorization_code",
                    "redirect_uri" -> "http://henning.kropponline.de",
                    "code" -> token)
      ).asString

    val jsonResponse = parse(response.body)
    val JString(access_token) = jsonResponse  "access_token"
    val JString(refresh_token) = jsonResponse  "refresh_token"
    val JString(user_id) = jsonResponse  "user_id"
    this.access_token = access_token
    this.refresh_token = refresh_token
    this.user_id = user_id
  }
}

At the end I use Json4s to parse the response body obtaining the required access_token, refresh_token, and user_id. The XPath access method seems convenient, but maybe not as efficient calling it for every single value.

Having now all needed credentials to obtain the data for my own user we can make use of the Fitbit API for example for Activities. As a sample call we will try to fetch the activity information of one day given as a parameter. The expected output should look like this:

{
  "activities":[],
  "goals":{
    "activeMinutes":30,
    "caloriesOut":3111,
    "distance":8.05,
    "steps":10000
  },
  "summary":{
    "activeScore":-1,
    "activityCalories":1152,
    "caloriesBMR":1925,
    "caloriesOut":2860,
    "distances":[
      {"activity":"total", "distance":6.64},
      {"activity":"tracker", "distance":6.64},
      {"activity":"loggedActivities", "distance":0},
      {"activity":"veryActive", "distance":1.1},
      {"activity":"moderatelyActive", "distance":0.38},
      {"activity":"lightlyActive", "distance":5.17},
      {"activity":"sedentaryActive", "distance":0}
    ],
   "fairlyActiveMinutes":7,
   "lightlyActiveMinutes":209,
   "marginalCalories":647,
   "sedentaryMinutes":1188,
   "steps":8940,
   "veryActiveMinutes":14
  }
}

For simplicity we are only interested in some of the summary values here, but this time we will map them to a object using Json4s which can be easily achieved with case classes if the attribute names correspond to the once defined in the result JSON. The object definition:

class FitBitToken {
...

  case class Summary(fairlyActiveMinutes:Int,
                      lightlyActiveMinutes:Int,
                      marginalCalories:Int,
                     sedentaryMinutes:Int,
                     steps:Int,
                     veryActiveMinutes:Int)
  case class Activities(summary: Summary)

}

Requesting the activities becomes a fairly simple implementation using the above mapping together with the prior requested access token of the user together with it’s id.

class FitBitToken {
...
  def getActivityOfDay(day:String) = {

    val response: HttpResponse[String] =
      Http(s"https://api.fitbit.com/1/user/$user_id/activities/date/$day.json")
        .headers("Authorization" -> s"Bearer $access_token")
        .asString

    val jsonResponse = parse(response.body)
    implicit val formats = DefaultFormats
    val activities = jsonResponse.extract[Activities]

    println(s"On $day: ${activities.summary.steps} steps with ${activities.summary.veryActiveMinutes} very active minutes.")

  }
}

Last we are going to use SBT for a sample execution:

fitbit-scala $ sbt console
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=2G; support was removed in 8.0
[info] Loading project definition from /Users/hkropp/Repository/fitbit-scala/project
[info] Set current project to fitbit-scala (in build file:/Users/hkropp/Repository/fitbit-scala/)
[info] Compiling 1 Scala source to /Users/hkropp/Repository/fitbit-scala/target/scala-2.11/classes...
[info] Starting scala interpreter...
[info] 
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_65).
Type in expressions for evaluation. Or try :help.

scala> val fbt = new scala.fitbit.FitBitToken
fbt: scala.fitbit.FitBitToken = scala.fitbit.FitBitToken@5fed2ff9

scala> fbt.tokenRequest
To get token please open: 
https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=227P25&redirect_uri=http%3A%2F%2Fhenning.kropponline.de&scope=activity

scala> fbt.getActivityOfDay("2016-03-18")
On 2016-03-18: 8940 steps with 14 very active minutes.

scala> fbt.getActivityOfDay("2016-03-19")
On 2016-03-19: 9704 steps with 15 very active minutes.

Further Reading

Advertisement

One thought on “Access to Fitbit API with Scala

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s