はじめに
fitbitのスマホアプリはなんとなく見づらい。
そのため、GoogleCalendarに睡眠の記録をイベントとして毎日書き込むアプリケーションを作成した。
今回実装したもの
GoogleAppScript(以降GAS)を使用し、毎日のFitbitの睡眠データを、GoogleCalendarに睡眠の記録をイベントとして記載するアプリケーションを作成した。
ソースコード
作成したソースコード
作成したソースコードは以下。
/*
* シートを作ってくれる
*/
function initApp()
{
let ss = createSheet('records');
ss.appendRow([ "TaskName","StartTime", "EndTime", "Duration"]);
ss = createSheet('fitbit-fetched-date');
ss = createSheet('config');
ss.getRange(1,1,4,1).setValues([
["Email"],
["Fitbit_Event_Color"],
["CLIENT_ID"],
["CLIENT_SECRET"],
]);
}
function createSheet(sheet_name)
{
return SpreadsheetApp.getActive().insertSheet(sheet_name);
}
function doEveryday()
{
let today = new Date();
if (isFetchedDate(today)) {
throw new Error(`Fitbit sleep data from ${today.toDateString()} has already been imported.`);
}
//refresh
let service = callCreateService();
service.refresh();
//download sleep data
let res = downloadSleepData(today);
if (res.getResponseCode() !== 200) {
throw new Error(res.getContentText());
}
let json = JSON.parse(res);
let ss = getSheetByName('records');
if (json.summary.totalSleepRecords === 0) {
return 0;
}
for (let i=0;i<json.summary.totalSleepRecords; ++i) {
let task_name = "睡眠[Fitbit]";
let start_time = new Date(json.sleep[i].startTime);
let end_time = new Date(json.sleep[i].endTime);
//write on records sheet
ss.insertRowBefore(2);
let ary = [task_name, start_time, end_time, calcDuration(start_time, end_time)];
ss.getRange(2,1,1,4).setValues([ary]);
//write on calendar
setEventOnCalendar(
findValueByKey('config','Email'),
task_name,
start_time,
end_time,
findValueByKey('config',"Fitbit_Event_Color")
);
}
//write on fitbit-fetched-data
ss = getSheetByName('fitbit-fetched-date');
ss.appendRow([Utilities.formatDate(today, "JST", "yyyy/MM/dd")]);
}
function isFetchedDate(date)
{
let ss = getSheetByName('fitbit-fetched-date');
let list = ss.getRange(1,1, ss.getLastRow(),1).getValues();
date.setHours(0, 0, 0, 0);
let index = list.findIndex(function(element){return element[0].getTime()=== date.getTime()});
return !(index === -1);
}
function getSheetByName(sheet_name)
{
return SpreadsheetApp.getActive().getSheetByName(sheet_name);
}
function callCreateService()
{
let service_name = 'sample Donwload Sleep Data With Fitbit';
let authorization_url = 'https://www.fitbit.com/oauth2/authorize';
let token_url = 'https://api.fitbit.com/oauth2/token';
let client_id = findValueByKey('config',"CLIENT_ID");
let client_secret = findValueByKey('config',"CLIENT_SECRET");
let service = createService(service_name, authorization_url, token_url, client_id, client_secret);
return service.setTokenHeaders({
'Authorization': 'Basic ' + Utilities.base64Encode(client_id + ':' + client_secret)
})
}
function createService(service_name, authorization_url, token_url, client_id, client_secret)
{
return OAuth2.createService(service_name)
.setAuthorizationBaseUrl(authorization_url)
.setTokenUrl(token_url)
.setClientId(client_id)
.setClientSecret(client_secret)
.setPropertyStore(PropertiesService.getUserProperties());
}
function downloadSleepData(date)
{
let service = callCreateService();
date = Utilities.formatDate(date, "JST", "yyyy-MM-dd");
let url = 'https://api.fitbit.com/1.2/user/-/sleep/date/' + date + ".json";
let options = {
headers: {Authorization: 'Bearer ' + service.getAccessToken()}
};
return UrlFetchApp.fetch(url, options);
}
/**
* configシートに記載した値を取得する
*/
function findValueByKey(sheet_name, key)
{
let ss = getSheetByName(sheet_name);
let row = findRow(ss, key, 1);
return ss.getRange(row, 2).getValue();
}
/**
* 略
*/
function findRow(sheet, needle, column)
{
let data = sheet.getDataRange().getValues();
for (let i=0;i<data.length;++i) {
if (data[i][column-1] === needle) {
return i+1;
}
}
return 0;
}
/**
* 睡眠時間を計算する
*/
function calcDuration(start_time, end_time)
{
let duration = (end_time - start_time) / 1000;
let hour = Math.floor(duration / 3600);
let minute = Math.floor((duration % 3600) / 60);
let second = Math.floor(duration % 60);
return Utilities.formatDate(new Date(0, 0, 0, hour, minute, second), "JST", "HH:mm:ss");
}
/**
* Google Calendarにイベントを追加する
*/
function setEventOnCalendar(email, task_name, start_time, end_time, color)
{
let cal = CalendarApp.getCalendarById(email);
cal.createEvent(task_name, start_time, end_time).setColor(color);
}
function doGet(e)
{
return HtmlService.createHtmlOutput(`<a href='${createAuthorizationUrl()}' target="_blank">認可ページへ</a>`);
}
function createAuthorizationUrl()
{
let service = callCreateService();
if (!service.hasAccess()) {
service = service.setCallbackFunction('authCallback').setExpirationMinutes(60).setScope('sleep');
}
return service.getAuthorizationUrl();
}
/**
* 認可後の処理
*/
function authCallback(request)
{
let service = callCreateService();
let isAuthorized = service.handleCallback(request);
if (isAuthorized) {
ScriptApp.newTrigger('doEveryday').timeBased().atHour(17).everyDays(1).create();
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
外部ライブラリ
外部ライブラリであるOAuth2 for Apps Scriptを使用する。
ライブラリの追加からスクリプトIDを入力して追加ボタンを押下する。
シートの各種説明
①recordsシート
fitbitの睡眠データを格納する
②fitbit-fetched-dateシート
取得した睡眠データの日付を格納する
③configシート
Fitbit APIやGoogle Calendar APIの実行に必要な値を格納する
Fitbit Developerでのアプリケーション申請
fitbit開発者サイトからアプリケーションの申請を行う。
https://dev.fitbit.com/
・「Application Name」「Description」は各自わかりやすい名前を設定する
・「Application Website URL」「Organization」「Organization Website URL」「Terms of Service URL 」「Privacy Policy URL」は適切な値を設定する
・「OAuth 2.0 Application Type」はPersonalを、「Default Access Type」は Read Onlyを選ぶ
・「Redirect URL」はアプリケーションの動作に影響するため、正確に設定する必要がある。Redirect URLは次を入力する
https://script.google.com/macros/d/${プロジェクトのID}/usercallback
プロジェクトのIDとはGASのID(Apps Scriptプロジェクトの一意の識別子)を指す。
各自のプロジェクトの設定タブから閲覧できる。
この画面に表示されている「Client ID」と「Client Secret」は後ほど、Google SpreadSheetのconfigシートに書き込む。
各種設定
事前にinitAppを一度実行しておく。
configシートへの書き込み
・睡眠データを書き込みたいGoogle CalendarのID(Gmail)をB1に書き込む
・Google Calendar上でのFitbitの睡眠データのイベントの色(0~11の値)をB2に書き込む
・Fitbit Developerでのアプリケーション申請で取得した「Client ID」と「Client Secret」をそれぞれB3、B4に書き込む
実行
ウェブアプリケーションとして公開し、アクセスを一度するだけで終了。
Web アプリケーションのデプロイ
このように表示されたURLにアクセスする。
fitbitログイン画面がでるので、各自のfitbitアカウントでログインすると
のようにfitbitの認可ページがでる。ここでは睡眠状態を選択し、許可を押す。
のような、画面がでれば成功。以降は毎日寝るだけ。
トリガーの設定
今回はソースコードの以下の部分
ScriptApp.newTrigger('doEveryday').timeBased().atHour(17).everyDays(1).create();
で17頃時頃にトリガーを作成した。
が、17時である理由は特になく(17時までに起床しているだろうという前提のみ)、atHour関数の引数は好きなように設定できる。
おわりに
fitbitデバイスを装着して寝ることを忘れない。
また、トリガーで設定した時刻までに同期をしておく。