in

Heart Rate Tracking with Angular2 & Fitbit

The Goal

I’ve always wanted a monitor of bio data… specifically data related to heart rate and GSR (Galvanic Skin Response) and if possible EEG data as well.

I’d love to have some giant monitor set up with streaming data on a variety of subjects.  For now… baby steps.

Tracking Data with Fitbit

Fitbit is a nice little wearable monitoring system that helps people keep track of their fitness goals.  For me, I just wanted it so that I could monitor my heart rate.  I thought about several fitness trackers, most many of them do not capture your heart rate ALL DAY LONG.  Nor do they do so on each second of the day.  There are some however that do… Fitbit does, as does Jawbone.  I looked into Apple watch, but everything I read was that it was going for different goals and didn’t track each heartbeat throughout the day.

For me, it was a toss up between Fitbit or Jawbone Up3… Looking back, I may have been better off picking the Jawbone, as it has a GSR sensor as well.

The Problem I’m trying to Solve

While the Fitbit tracker is pretty nifty it (nor its app) didn’t meet the following criteria:

  • Intra-Day data – By default, the app shows the past 24hr of data. I wanted it down to the min.
  • Push this data public, so anyone with a browser can see my current heart rate.

That 24hr window was too annoying.  If the data was captured intra-day (like every min, every hour, etc.) I could get a higher resolution of my heart rate and could correlate changes and fluctuations to life events more easily.

Thankfully the Fitbit team allow a user to access their intra-day data that is collected by the device.  They capture a heart rate every 1 second.  This data is passed to the phone (via the Fitbit App) and pushed to the Fitbit servers.

Fitbit API Limitations

The Fitbit API is limited to 150 calls per hour.

I am only allowed to display my personal data (i.e. I can’t pass other user’s through their login to access their intra-day data.)

Angular2 & Fitbit

While there are many different Node and Angular libraries out there – I struck out getting any / all of them to work.  Instead, I opted to do this all by hand.  I would create a method to capture an authorization token, then pass it along with the header to get my intra-day data.  After collecting the data, I choose to store it in a Firebase database.  The database is being subscribed to via web sockets, so any data updates will kick off a dynamic update on the page.

As I was studying Angular2 at the time, I choose to do the work in Angular2.  The actual call to grab the user data should be done as a backend process, but for now, it’s front end.  But any server could generate this call and store the result in Firebase.

Angular2 App Setup

My app was a CLI type, so I used the command line: ng g component home

This created my “home” component (a dashboard of sorts.) This is going to be the landing page I’ll set with routes later on.

Firebase Setup

Why Firebase?  Well for my needs, it’s free.  It’s a Google product and they throw in a free SSL cert.  Their Databae is realtime and pretty amazing.  Everything is stored in the Non-SQL db as objects, so pulling it out and iterating / parsing the data is pretty straight forward.  It also works well with Angular and JS frameworks.

For Angular2, run: ng install angularfire2 from the project root and that will set it up for your app.

I won’t cover much on Firebase, but you can check it out at firebase.google.com and create an account.

Once you have the account, you go to your console and click on Database.

In the DB screen, you’ll see your db url (https://yoursite-firebase.blahblah) and you can simply append a model, like so:

/posts to the end of your url and hit enter… it creates the table for you!

If you go back to your Firebase dashboard for this project, you should see this:

screen-shot-2016-12-27-at-3-32-39-pm

 

Clicking “Add Firebase to your web app” will give you a popup with the connection data you’ll need to input in your apps.  You’ll need this to connect to firebase to read or save data.

Next, back in the angular app, I created added to the app.module.ts file a config to connect to Firebase… from the “Add Firebase to your web app” popup:

export const firebaseConfig = {
  apiKey: "some key goes here",
  authDomain: "my domain info goes here",
  databaseURL: "url to my database here",
  storageBucket: "same drill as above",
  messagingSenderId: "my firebase sender id here"
}

While in app.module.ts lets make a reference to a file not yet created:

import {FirebaseService} from "./services/firebase.service";

Firebase Service

I created a services folder under the /src/app folder for the project and in there I created a firebase.service file.  This is where the firebase calls are going to get called from.

I use the following imports:

import {Injectable} from '@angular/core';
import {AngularFire, FirebaseListObservable} from 'angularfire2';
import 'rxjs/add/operator/map';
import {AccessToken} from "../AccessToken";
import {HeartRate} from "../HeartRate1Day";

AccessToken and HeartRate are Models that for the data pulled and saved from/into Firebase.  You can create them if you like.  Further on, I’ll show how I used them.

Adding a Fitbit Callback

When you create your app on the Fitbit dev side, you’ll be asked to provide a callback… for now you can set it to your localhost/callback

Back in the app, from the command line create a callback component:

ng g component callback

I use these imports:

import { Component, OnInit } from '@angular/core';
import {ActivatedRoute, Router} from "@angular/router";
import { URLSearchParams, } from '@angular/http';
import { FirebaseService } from '../services/firebase.service';

Above the constructor I have defined a string of access_token:

access_token: string;

The constructor looks like this:

constructor(public routes:Router, private firebaseService: FirebaseService) {

  routes.events.subscribe(s => {

      let params = new URLSearchParams(s.url.split('#')[1]);
      let access_token = params.get('access_token');
      var created_at = new Date().toString();
      var postContent = {
      access_token: access_token,
      created_at: created_at
    };
      console.log(postContent)
      this.firebaseService.addToken('put the db key here', postContent);
  });
}

In bold above, I have “put the db key here” this database element that we reference for the token.  You can create this in Firebase for your project.  I didn’t get into that granular level with Firebase, but it’s pretty easy, you can do it in the UI, with the + sign on a db table like [your firebase db]/token, or you could first post to firebase and it will generate a random string for the database element.

Adding A Fitbit Token

In the firebase.service file, modify the constructor to:

constructor(private af: AngularFire) {

}

Above the constructor let’s create our acess_token variable:

access_token: FirebaseListObservable<AccessToken[]>;

You might be able to get away with <any[]>, but in my case I defined a Model called Accesstoken[].  This model exists in the /src/app folder and it’s contents are:

AccessToken.ts:

export interface AccessToken{
  $key?: string;
  token?: string;
  created_at?: string;
}

That “addToken” method isn’t yet created, so let’s make it.  Back in the firebase service, let’s create a method addToken, just below the

addToken(key, newToken) {
  this.access_token = this.af.database.list('/token').map( (arr) => {return arr;}) as FirebaseListObservable<AccessToken[]>;

  return this.access_token.update(key, newToken);
}

The above is setting the access token from Fitbit as a FirebaseListObservable with our model AccessToken.  This value is then returned as an update to an existing reference (what’s passed in as the key.)

Routes

Routes are endpoint declarations that allow visitors to hit a specific endpoint and it pulls a corresponding Component.

We need a route to /callback, for our Fitbit configuration to work.

Back in angular.module.ts add this import:

import {RouterModule, Routes} from '@angular/router';

Then add this below the imports:

const appRoutes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'callback', component: CallbackComponent},
];

The first route there is taking the empty string (just hitting the domain) and sending it to the home component we created.  Never use ‘/’ in Angular2 as a route path… it will barf pretty bad.

The second route there is defining our callback.  When our Fitbit app makes a call to get a token, it redirects to this endpoint /callback.  We have set ‘callback’ (our endpoint) to go to the Callback component.

In the @NgModule in angular.module.ts, add the following in the imports bit… somewhere in there, append:

, RouterModule.forRoot(appRoutes)

Finally, in app.component.html add this tag:

<router-outlet></router-outlet>

With this in place, here’s how the flow works:

When index.html is called, it pulls the app-route tag, this calls the app.component.html – it’s tag to router-outlet will pull the router references we created in the module…

Testing The Token Saving

Back in the dev Fitbit site, go to your app (Manage Apps) and edit it.  There you can test it using the link at the bottom (OAuth 2.0 tutorial page).  This loads a test form.  Fill it out with your app’s details… including the callback of localhost:4200/callback (or whatever port you set up) – make sure that your callback here matches the callback in the app details!!

Also, only the Fitbit app type of PERSONAL will allow you to get your intra-day data.

Start up your local Angular2 app (if it isn’t running already.)

When you test this Fitbit app in your OAuth tutorial page, it should pull up your localhost app on port 4200 with the /callback endpoint… this in turn should kick off your constructor on the callback.component.ts file.  Which has a method here to send the token key and value to: this.firebaseService.addToken method.

This, in turn adds (or updates) our token.

Check Firebase via browser …. go to your Firebase console, Project, Database and see if your table has a new entry (or an updated entry.) It should have the contents of the token in there.

Getting Fitbit Heart Rate Data

Ok, the token was the first part of our Ultimate quest: getting intra-day heart rate data.

Back in the home.component.ts file, let’s add this method below our constructor:

getMinHR(){

  var whatTime = new Date();
  var hour = whatTime.getHours();
  var minute = whatTime.getMinutes();

  this.firebaseService.getToken().subscribe(validtoken => {
    this.validtoken = validtoken;
    var headers = new Headers({'Authorization': 'Bearer ' + validtoken['access_token']});
    var formatedApiCall = 'https://api.fitbit.com/1/user/-/activities/heart/date/today/1d/1sec/time/'+(hour - 1)+':'+(minute - 1)+'/'+hour+':'+minute+'.json'
    this.http.get(formatedApiCall, {headers:headers})
      .map(res => console.log(this.firebaseService.saveMinHR(res.json())))
      .subscribe();
  });
}

The way the Fitbit api works for intraday, is that you pass in a time stamp into the Rest endpoint.  Read up on their intraday endpoint.  Since I wan to grab data from 1 hour ago, I take the current Date, pull the hours on it with the “getHours()” method, as well as the getMinutes() method.  I subtract the hour by 1 and pass that value into the rest call.  Along with the current time.  this gives me my 1 hour range in the Rest end point.

Note the headers I’m using.  I’m getting a token from getToken() (a method we haven’t made yet) and subscribing to it.  I take the token as “validtoken” and I pass that into the call with the headers… To do this you’ll also need this import:

import {Http, Headers} from "@angular/http";

This lets you use http and pass header info into the http requests.  We want our header to have Authorization with a value of “Bearer and our valid toke”.

Getting the Token

I used a getToken() call so let’s go make that method.  In the firebase service, I made a method called getToken():

getToken(){
  const token = this.af.database.object('/token/PUT YOUR DB ELEMENT HERE THAT YOUR TOKEN OBJECT SAVED TO');
  return token;
}

replace my text in the databse.object with whatever path is your token in the Firebase db.

Getting The Heartrate Data

This is then returned back to the getMinHR method.  Once we have the token it is sent along with the REST request and that allows us to pull our intra-day data as a JSON object.  This is sent to another Firebase Service method: saveMinHR().

Saving (Updating) the Fitbit Intraday Heartrate Data

Back in the Firebase service file, I have another method to save the heartrate data we just got.  The method simply does:

saveHR(data){
  this.heart_rate = this.af.database.list('/hr').map( (arr) => {return arr}) as FirebaseListObservable<HeartRate[]>;
  return this.heart_rate.update('YOUR KEY THAT YOU'LL UPDATE',data);
}

Same as the token, my goal here isn’t to have a bunch of data entries for Heartrate.  Instead, I have a table /hr and in it an element that will be updated (a key, if you will.)  That can be created in the Firebase UI.  The update command will look up the key you have created in the /hr data table, and will pass in the data (your heart rate data from Fitbit.)

I’m also using a model of HeartRate[] as the typing on the FirebaseListObservable.

My HeartRate model is in the /src/app location and is HeartRate.ts (this file also needs to be imported into the firebase service file.):

export interface HeartRate{
  $key?: string;
  data?: string;
}

Right now, if all went well, you are getting a token and using the token in a REST call to Fitbit, to get your heart rate data for the last hour (make sure you have sync’d your Fitbit to their mobile app, so that your Fitbit account has data for the past hour.)

Charting the Heartrate Data

Honestly I felt this was the hardest part.  I tried multiple chartin solutions, but ended up with Highcharts.js.  You’ll need to install Highcharts: https://www.npmjs.com/package/angular2-highcharts

Highcharts had a simple line graph example that worked well for me.  It takes an array of data and plots the points.

The data I was getting from Fitbit was being stored into Firebase as an object like so:

/hr

+ [my key]

+activities-heart

+dataset

+0

->  time: “16:36:13”
-> value: 83

screen-shot-2016-12-27-at-6-04-29-pm

The time and value are what I want to plot here…

After a lot of frustration, I got this to work like so:

Heart Rate Componet

Firt I created a new component, ng g component heartchart

within heartchart.component.ts it looks like:

mport { Component, OnInit } from '@angular/core';
import { FirebaseService } from '../services/firebase.service';
@Component({
  selector: 'simple-chart-example',
  template: `<div *ngIf="options">
                <chart [options]="options"></chart>
            </div>
    `
})
export class HeartChartComponent implements OnInit {
  hr: Object;

  constructor(private firebaseService: FirebaseService) {

    this.firebaseService.getMinHR().subscribe(hr =>{
      this.hr = hr;
      var timeValues = [];
      hr.forEach((k: any)=> {
        timeValues.push([k.time, k.value])
      });

      this.chartOptions(timeValues)
    });
  }
  options: Object;

  ngOnInit() {
  }

  chartOptions(data){
    this.options = {
      title : { text : 'Heart Rate Chart (Past Hour)' },
      xAxis: {
        title: {text: 'Time (Hover over Line for Timestamp)'},
        labels: {enabled:false}
      },
      yAxis: {title: {text: 'Heart Rate'}},
      series: [{
        name: 'Brians Heart Rate',
        data: data
      }]
    };
  }
}

Just looking at this makes me feel nausiated.  It took a lot of time to get data in the way that HighCharts wanted it…  Before getting into this bit… we need to create a new method on the firebase.service.ts.

Not to confuse you, but I have two methods called getMinHR().  One is on the home.component.ts file and the other is in the firebase.service.ts file.

In your Firebase Service, create another method for getMinHR() – this one will simply read data from Firebase:

getMinHR(){
  this.current_hour_hr = this.af.database.list('/hr/YOUR DATA ELEMENT IN FIREBASE THAT YOU SAVE TO/activities-heart-intraday/dataset').map( (arr) => {return arr}) as FirebaseListObservable<HeartRate1Day[]>;
  return this.current_hour_hr;
}

This method, unlike the other, is getting the Heart Rate data from Firebase (what was saved.)  The other getMinHR() method was getting the data from Fitbit and saving it into Firebase.

Question: why not just subscribe to Fitbit directly and not rely on saving the data to Firebase at all?

Answer: I couldn’t get it working. Lame, but true.  I also was scared at the methodology that Angular2 might use to detect changes at the endpoint. I have limited usage of their endpoint (150 calls per hour.)  I opted to control the calls by setting a specific timer on pulling my data and saving it to Firebase.

This method is calling the endpoint in Firebase (/hr/My KEY/activities-heart-intraday/dataset, recall from the break out, this will return data at this point:

screen-shot-2016-12-27-at-6-04-29-pm

Back in the HomeComponent.html I added this tag:

<simple-chart-example></simple-chart-example>

That’s the selector from my heartchart.component.ts file.

Now when I go to the HomeComponent, it will load the chart (hopefully) with data returned.

This works for me… it took me a couple weeks to get here, working on this nights and weekends.  A lot of trial and error (especially with graphing) went into this… I also utilized a lot of Stack Overflow and tutorials to get this fully working.

End Result

After all this, if you can get it working you’ll have a hearrate chart like this:

screen-shot-2016-12-27-at-6-26-56-pm

What’s great about smaller time sets is you have much greater resoution of your heartrate.  Instead of the default 24HR nonsense of Fitbit, you can zoom into 1min, 5min, or in this case 1hour incriments and see your heart reacting to your environment.

In the image below I had a spike due to the stress of getting to work and finding that my parking garage was locked up, so it was very difficult to get in. This caused a spike in my heart rate:

parking-heart-spike

You can also see the chart in realtime at:

https://hr.sdet.us

 

Problems with Syncing & the Fitbit mobile App

The biggest problem with this is the Fitbit sync.  For this chain of events to work, the wearable device needs to talk to the mobile app on my phone. It syncs my data and pushes it to the Fitbit server and I connect to that server with my token to retrieve it.

Unfortunately, the Fitbit app stops syncing.  I’m not sure why.  It just stops.  When it stops I have to manually make it sync (pressing a manual sync button in the app itself) or relaunching the Fitbit app.  This is of course a nuisance.  If I neglect to manually sync and it stops auto syncing, the data will be very stale!

 

What do you think?

0 points
Upvote Downvote

Total votes: 0

Upvotes: 0

Upvotes percentage: 0.000000%

Downvotes: 0

Downvotes percentage: 0.000000%

Written by Admin

I work for a Telecom company writing and testing software. My passion for writing code is expressed through this blog. It's my hope that it gives hope to any and all who are self-taught.

Comments

Leave a Reply

Loading…

Getting DESC order with Angular2 and Firebase

Node: Prototype Inheritance