本記事は下記の続き「サンプル2編」です。
フィットネストラッカーのまとめと連携方法について
Node.js+PassportでフィットネストラッカーのJawbone UP3から日付と歩数を取得し、Webページ上で表示させるといった内容です。
Jawboneのアカウント登録
Jawboneのデベロッパー用ページにアクセスし、
アカウントを作成します。
アカウント作成後、左側メニューの ACCOUNT をクリックし、アカウントページへ移動。
Create APP をクリックし、以下の内容を登録する。
- Name(アプリ名)
- Description(アプリの概要)
サンプルなら適当でも可 - Long Description(アプリの詳細)
サンプルなら適当でも可 - Logo(ロゴ)
適当な画像をアップする。無くても可。 - Application Links(ここ重要)
- URL(アプリのホームページ)
最初にアクセスするページのURLとか。今回はhttp://localhost:8000
- Authorization URL
ログイン用URL。今回はhttp://localhost:8000/login/jawbone
- OAuth Redirect URIs
認証後にリダイレクトさせるURL。今回はhttp://localhost:8000/first
- Learn More About URL(気にしない。空白)
- URL(アプリのホームページ)
- Pub/Sub
Webhookを使う時に使用するので、今回は Disabled にチェックする。
アプリの登録を済ますと、左側メニューの APPLICATIONSにアプリ名が表示される。
そこから、アプリの管理画面に入れるので、下の値をメモする。
後々、Jawboneのデータを取得するのに必要。
- Client Id
- App Secret
Node.jsの準備
記事のタイトルにもありますが、Jawboneとの連携はNode.jsで行います。
ここで、Node.jsがパソコンに入ってないです! という方は、
下記のURLの方の記事を参考に環境を整えて下さい。
node.jsインストール備忘録(windows7)
macでNode.jsの環境設定
今回筆者の環境は以下です。
Windows10 64bit
Node.js v4.5.0
作業用のフォルダは任意の場所に以下の通りに作成します。
(※太字がフォルダ)
-
JawboneTest/
- node_modules/
-
views/
- layout.ect
- index.ect
- userdata.ect
- .env
- app.js
- package.json
node_modules には必要なモジュールが入ります。
.ect はhtmlのようなもの。.env は環境変数を書いとくファイル。
※.envはwindowsのファイルエクスプローラー上で作成すると、
名前が入力されていないといったエラーで作成できないかもしれない。
その場合は、普段使用ているエディターで新規作成、保存してください。
app.js がメインのプログラム。
package.json はプロジェクトに必要なモジュールを管理するファイル。
必要なモジュールの準備
この後の記事でいくつかモジュールを使うので、ここではその準備をします。
モジュールのインストールにはnpmを利用するのですが、
Node.jsをインストールしたら一緒に入るので安心して下さい。
さて、先程package.jsonというファイルを作成しましたが、
この中身を以下のようにします。
{
"name": "JawboneTest",
"version": "0.0.0",
"main": "app.js",
"dependencies": {
"body-parser": "~1.15.2",
"cookie-parser":"~1.4.3",
"ect":"~0.5.9",
"dotenv":"~2.0.0",
"express": "~4.14.0",
"express-session":"~1.14.2",
"jawbone-up":"~0.1.2",
"passport":"~0.3.2",
"passport-oauth":"~1.0.0"
}
}
主要なのだけ説明すると
dtenv は先程作成した .envファイルから環境変数を呼び出すため。
express はwebフレームワーク。
ect はテンプレートエンジンといわれるもの。
express-session はexpressでセッションを利用するためのもの。
jawbone-up はJawbone APIを簡単に行うためのもの。
passport はJawboneと連携するための認証を簡単にするためのもの。
package.json を書いたら、app.jsがあるフォルダを開いて、
アドレスバーに cmd
と記入してEnter。
そのディレクトリに移動した状態でコマンドプロンプトが表示されるので、
npm install
と記入します。
先程 package.json の dependencies に記入したモジュールと
それに関連したモジュールが一気に node_modules に入ります。
Node.jsのバージョンが筆者と同じなら、全て入ると思います。
やっとこ本編
まず最初に、メインのプログラムである app.js の中身を
全て下記に記します。
var express = require('express'),
path = require('path'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
dotenv = require('dotenv'),
passport = require('passport'),
session = require('express-session'),
JawboneStrategy = require('passport-oauth').OAuth2Strategy,
ECT = require('ect');
dotenv.load();
var app = express();
var ectRenderer = ECT({ watch: true, root: __dirname + '/views', ext : '.ect' });
app.engine('ect', ectRenderer.render);
app.set('view engine', 'ect');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.set('port', process.env.PORT || 8000);
app.use(session({
secret: 'jaw key'
}));
app.use(passport.initialize());
app.use(passport.session());
var jawboneAuth = {
authorizationURL: 'https://jawbone.com/auth/oauth2/auth',
tokenURL: 'https://jawbone.com/auth/oauth2/token',
callbackURL: 'http://localhost:8000/first'
};
passport.use('jawbone', new JawboneStrategy({
clientID: process.env.JAWBONE_CLIENT_ID,
clientSecret: process.env.JAWBONE_CLIENT_SECRET,
authorizationURL: jawboneAuth.authorizationURL,
tokenURL: jawboneAuth.tokenURL,
callbackURL: jawboneAuth.callbackURL
}, function(token, refresh, profile, done){
console.log("Get token");
var user = {
accessToken: token,
refreshToken: refresh
};
done(null, user);
}));
passport.serializeUser(function(data, done){
done(null, data);
});
passport.deserializeUser(function(data, done){
done(null, data);
});
var sessionCheck = function(req, res, next){
if(req.isAuthenticated())
return next();
res.redirect('/login/jawbone');
};
app.get('/', function(req, res) {
if(!req.isAuthenticated()){
res.render('index', { title: 'Home' });
console.log("Home");
}
else{
res.redirect('/data');
}
});
app.get('/first',
passport.authenticate('jawbone',{
scope:['basic_read', 'move_read'],
failureRedirect:'/'
}), function(req, res){
res.redirect('/data');
console.log("logged in");
}
);
app.get('/login/jawbone',
passport.authenticate('jawbone',{
scope:['basic_read', 'move_read'],
failureRedirect:'/'
}), function(req,res){
});
app.get('/data', sessionCheck, function(req, res){
var options = {
access_token: req.session.passport.user.accessToken,
client_id: process.env.JAWBONE_CLIENT_ID,
client_secret: process.env.JAWBONE_CLIENT_SECRET
};
var up = require('jawbone-up')(options);
up.moves.get({}, function(err, data){
if(err){
console.log("Error receiving move data");
}
else{
var jawboneData = JSON.parse(data).data;
for(var i=0; i<jawboneData.items.length; i++){
console.log(jawboneData.items[i].date + " : " + jawboneData.items[i].title);
}
var sendData = JSON.stringify(jawboneData);
res.render('userdata', {title: 'User data', data: sendData});
}
});
});
app.get('/logout', function(req,res){
req.logout();
res.redirect('/');
});
//Open server
var server = app.listen(app.get('port'), function() {
console.log('Server listening on port ' + app.get('port'));
});
主な箇所を順番に説明すると、
モジュールをrequireして変数に入れることで、この変数名でモジュールが使えるようになります。
var express = require('express'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
dotenv = require('dotenv'),
passport = require('passport'),
session = require('express-session'),
JawboneStrategy = require('passport-oauth').OAuth2Strategy,
ECT = require('ect');
これで .env に記述した環境変数が使えるようになります。
dotenv.load();
.env に記述する環境変数はJawboneのアプリ登録を行った際にメモした
- Client Id
- App Secret
です。こういったIDとかPassword的な値を直接 app.js に書き込むのは良くないので、.env内に以下のように記述して分けておきます。
JAWBONE_CLIENT_ID="自分のID"
JAWBONE_CLIENT_SECRET="自分のSecret"
app.js内で以下のように記述すると値を取得できます。
process.env.JAWBONE_CLIENT_ID
process.env.JAWBONE_CLIENT_SECRET
ここでは、テンプレートエンジンを使えるようにするための設定をしています。
今回は、処理スピードが爆速だと一時期話題になっていた ECT を使用していますが、他に ejs や jade といったものもあります。
var ectRenderer = ECT({ watch: true, root: __dirname + '/views', ext : '.ect' });
app.engine('ect', ectRenderer.render);
app.set('view engine', 'ect');
ここは、passportで認証を行ったときの情報をセッション内に保存し、
繰り返し使えるようにするための設定部分です。
session の secret は何でもいいです。
app.use(session({
secret: 'jaw key'
}));
app.use(passport.initialize());
app.use(passport.session());
ここは、passportを使ってJawboneのOAuth認証を行う部分です。
var jawboneAuth = {
authorizationURL: 'https://jawbone.com/auth/oauth2/auth',
tokenURL: 'https://jawbone.com/auth/oauth2/token',
callbackURL: 'http://localhost:8000/first'
};
passport.use('jawbone', new JawboneStrategy({
clientID: process.env.JAWBONE_CLIENT_ID,
clientSecret: process.env.JAWBONE_CLIENT_SECRET,
authorizationURL: jawboneAuth.authorizationURL,
tokenURL: jawboneAuth.tokenURL,
callbackURL: jawboneAuth.callbackURL
}, function(token, refresh, profile, done){
console.log("Get token");
var user = {
accessToken: token,
refreshToken: refresh
};
var options = {
access_token: token,
client_id: jawboneAuth.clientID,
client_secret: jawboneAuth.clientSecret
};
done(null, user);
}));
passport.serializeUser(function(data, done){
done(null, data);
});
passport.deserializeUser(function(data, done){
done(null, data);
});
OAuth認証をするためには以下の値が必要です。
- clientID
- clientSecret
- authorizationURL
- tokenURL
- callbackURL
上2つは .env に、下3つは jawboneAuth に記述しています。
基本的にはこれらの値を使って認証を行い、認証が通れば、accessTokenが取得できます。
このトークンを使うことで、 Jawbone API を使ってデータを取得することができます。
ここで取得したトークンはセッションに保存されるため、次からは認証をスキップして Jawbone API を利用できます。
ここでは、ブラウザからのアクセスに対して何を返すのかを記述しています。
ブラウザでhttp://localhost:8000
にアクセスすると、一番上の app.get('/',function(...
が反応します。
認証済ならJawboneから取得したデータを表示する/data
にリダイレクトさせ、未認証なら index.ect のページを提供します。
var sessionCheck = function(req, res, next){
if(req.isAuthenticated())
return next();
res.redirect('/login/jawbone');
};
app.get('/', function(req, res) {
if(!req.isAuthenticated()){
res.render('index', { title: 'Home' });
console.log("Home");
}
else{
res.redirect('/data');
}
});
ブラウザに提供されたindex.ect 上のログインボタンを押すと/login/jawbone
にアクセスし、scopeで指定したデータに対して、取得を許可するためのページがJawboneから提供されます。
今回scopeで指定しているのは、
- basic_read(ユーザの基本情報の呼び出し)
- move_read(歩行に関する情報の呼び出し)
他にscopeで指定できる内容については以下で確認できます。
Jawbone Authenication
app.get('/login/jawbone',
passport.authenticate('jawbone',{
scope:['basic_read', 'move_read'],
failureRedirect:'/'
}), function(req,res){
});
Jawboneが提供するページの許可ボタンを押すと、アプリ管理画面の
OAuth redirect URL
に設定したhttp://localhost:8000/first
にリダイレクトされます。
ここで先程記述したpassportの認証が行われ、トークンがセッションに保存されます。
app.get('/first',
passport.authenticate('jawbone',{
scope:['basic_read', 'move_read'],
failureRedirect:'/'
}), function(req, res){
res.redirect('/data');
console.log("logged in");
}
);
認証が成功すると/data
にリダイレクトされ、Jawboneのデータを取得する流れになります。
なお、Jawbone APIを利用するためには以下の値が必要です。
- access_token(セッションに保存されたトークンを呼び出す)
- client_id(.envの環境変数を呼び出す)
- client_secret(.envの環境変数を呼び出す)
これらを変数optionsに格納して、jawbone-up をrequireし、歩行のデータを呼び出します。この歩行データと一緒に userdata.ect をブラウザに提供します。
up.moves.get({},function(...
では歩行のデータを取得できますが、それ以外のデータを取得する関数については以下を参考にしてください。
jawbone-up
app.get('/data', sessionCheck, function(req, res){
var options = {
access_token: req.session.passport.user.accessToken,
client_id: process.env.JAWBONE_CLIENT_ID,
client_secret: process.env.JAWBONE_CLIENT_SECRET
};
var up = require('jawbone-up')(options);
up.moves.get({}, function(err, data){
if(err){
console.log("Error receiving move data");
}
else{
var jawboneData = JSON.parse(data).data;
for(var i=0; i<jawboneData.items.length; i++){
console.log(jawboneData.items[i].date + " : " + jawboneData.items[i].title);
}
var sendData = JSON.stringify(jawboneData);
res.render('userdata', {title: 'User data', data: sendData});
}
});
});
app.get('/logout', function(req,res){
req.logout();
res.redirect('/');
});
Webページ側
ここでは、ブラウザに提供するWebページについて説明します。
冒頭で views の中に中身のない以下のファイルを作成しました。
- layout.ect
- index.ect
- userdata.ect
.ectってどう書くの? という方がいらっしゃると思いますが、HTMLと同じで、ここでは+αな機能がついたものと考えて下さい。
layout.ectの下部に**<% content %>といったhtmlでは見慣れない記述があります。
これがECTの機能の1つで、ここには index.ect 又は userdata.ect
の内容が入ります。
複数のページを提供する際に、それぞれ同じライブラリや見た目の設定を
利用することがあります。そういった共通部分を layout.ect に記述して共有することで、同じ記述をする手間を減らせます。
layout.ectの内容を他のectで呼び出す際は<% extend 'layout.ect' %>**を先頭に付けます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title><%= @title %></title>
<meta name="viewport" content="width=device-width">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<style type="text/css">
body{
font-family: fantasy,sans-serif;
margin: auto;
text-align: center;
max-width: 100%;
height: 100%;
width: auto;
background-color: black;
color: white;
-webkit-text-size-adjust: 100%;
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-o-text-size-adjust: 100%;
text-size-adjust: 100%;
}
hr{
border-top: 4px double #FFFFFF;
}
#time{
color: black;
}
.inputText{
color: black;
}
#footer-nav{
width: 100%;
}
#wakeupImg{
width: 50%;
}
</style>
</head>
<body>
<% content %>
</body>
</html>
index.ectは、Jawboneの認証ページに進むためのログインボタンを設けています。
<% extend 'layout.ect' %>
<h1>Jawbone Home</h1>
<form method="GET">
<input type="submit" name="submit" formaction="/login/jawbone" class="btn btn-primary btn-lg btn-block" value="Jawbone Login">
</form>
<script>
</script>
userdata.ectでは、呼び出した歩行データを日付ごとにテーブルにして表示しています。
新たに**<%- @data %>**というECTの機能を利用します。
app.js内で userdata.ect を提供する部分がありますが、そこでページと一緒にdataに歩行データ(sendData)を格納して提供していました。
res.render('userdata', {title: 'User data', data: sendData});
このdataをページ側で取得することができます。
<% extend 'layout.ect' %>
<body>
<h1>User data</h1>
<table id="moves" class="table table-condensed table-bordered">
<tr>
<th>Date</th>
<th>Steps</th>
</tr>
</table>
<a href="/logout" class="btn btn-primary btn-lg btn-block">Log out</a>
</body>
<script>
var jawboneData = <%- @data %>;
console.log(jawboneData);
for(var i=0; i<jawboneData.items.length; i++){
var date = jawboneData.items[i].date;
var title = jawboneData.items[i].title;
console.log(date + "---" + title);
$("#moves").append(
$("<tr></tr>")
.append($("<td></td>").text(date))
.append($("<td></td>").text(title))
);
};
</script>
$といった見慣れない記号がありますが、これはjQueryを利用する際の記号です。
jQueryはJavaScript用の便利なライブラリなので、詳しくは知りたい方は以下を参考にしてください。
以上で全ての環境が整いました。
ファイルエクスプローラーでapp.jsがあるフォルダに移動し、アドレスバーにcmd
を入力してEnter。
以下のコマンドを実行することでサーバーが起動します。
ブラウザでhttp://localhost:8000
にアクセスしましょう。
>node app
おわりに
ここまで長々と書きましたが、Node.jsとexpressでアプリを作った経験のある人なら、OAuth認証がややこしいですが、説明文を読まないでもある程度理解できるかなと思います。
初めてこういったドキュメントを書いたので、わかりにくいかもしれませんが、フィットネストラッカーに興味がある人の足しになれば嬉しい限りです。
ありがとうございました。
(作成:神奈川工科大学 杉村研究室)
参考URL
jawbone-up
passport
node.js テンプレートエンジンECTが爆速!!らしい件(ectの使い方)
node.js+express-generator+passportでJawbone UPフィットネスデータをグラフ表示