Skip to content

Improvements: Headless execution, Heart rate support #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 77 additions & 19 deletions fitbit.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ var CONSUMER_SECRET_PROPERTY_NAME = "fitbitConsumerSecret";
*/
var PROJECT_KEY_PROPERTY_NAME = "projectKey";

/**
* Sheet ID.
* @type {String}
* @const
*/
var CLIENT_SHEET_ID = "sheetId";

/**
* Default loggable resources.
Expand All @@ -44,8 +50,13 @@ var LOGGABLES = [ "activities/log/steps", "activities/log/distance",
"activities/log/minutesSedentary",
"activities/log/minutesLightlyActive",
"activities/log/minutesFairlyActive",
"activities/log/minutesVeryActive", "sleep/timeInBed",
"sleep/minutesAsleep", "sleep/minutesAwake", "sleep/awakeningsCount",
"activities/log/minutesVeryActive",
"sleep/timeInBed",
"sleep/minutesAsleep",
"sleep/minutesAwake",
"sleep/awakeningsCount",
"sleep/efficiency",
"activities/heart",
"body/weight", "body/bmi", "body/fat" ];

/**
Expand All @@ -63,6 +74,9 @@ var PERIODS = [ "1d", "7d", "30d", "1w", "1m", "3m", "6m", "1y", "max" ];
*/
var scriptProperties = PropertiesService.getScriptProperties();


var sheet_name = "Sheet1"; //Change this if you want to rename your sheet!

function refreshTimeSeries() {

// if the user has never configured ask him to do it here
Expand All @@ -74,7 +88,8 @@ function refreshTimeSeries() {
Logger.log('Refreshing timeseries data...');
var user = authorize().user;
Logger.log(user)
var doc = SpreadsheetApp.getActiveSpreadsheet()
var ss = SpreadsheetApp.openById(getSheetId());
var doc = ss.getSheetByName(sheet_name);
doc.setFrozenRows(2);
// header rows
doc.getRange("a1").setValue(user.displayName);
Expand Down Expand Up @@ -141,12 +156,22 @@ function refreshTimeSeries() {
if ( row_index != 0 ) {
row_index++;
} else {
row_index = findRow(date);
row_index = findRow(date, doc);
}
// Insert Date into first column
doc.getActiveSheet().getRange(row_index, 1).setValue(val["dateTime"]);
doc.getRange(row_index, 1).setValue(val["dateTime"]);
// Insert value
doc.getActiveSheet().getRange(row_index, 2 + activity * 1.0).setValue(Number(val["value"]));
var formattedValue = Number(val["value"]);

Logger.log("Title: " + title);
// This is heart rate data
if ( val.value["restingHeartRate"] !== undefined) {

formattedValue = Number(val.value["restingHeartRate"]);
}

doc.getRange(row_index, 2 + activity * 1.0).setValue(formattedValue);
Logger.log("Done with " + activity);
}
}
}
Expand Down Expand Up @@ -185,6 +210,17 @@ function getProjectKey() {
return key;
}

/**
* @return String Project key
*/
function getSheetId() {
var key = scriptProperties.getProperty(CLIENT_SHEET_ID);
if (key == null) {
key = "";
}
return key;
}

/**
* @param String Project key
*/
Expand Down Expand Up @@ -251,6 +287,7 @@ function saveConfiguration(e) {
setConsumerKey(e.parameter.clientID);
setConsumerSecret(e.parameter.consumerSecret);
setProjectKey(e.parameter.projectKey);
scriptProperties.setProperty(CLIENT_SHEET_ID, e.parameter.sheetId);
setLoggables(e.parameter.loggables);
setPeriod(e.parameter.period);
var app = UiApp.getActiveApplication();
Expand All @@ -265,7 +302,7 @@ function renderFitbitConfigurationDialog() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setTitle("Configure Fitbit");
app.setStyleAttribute("padding", "10px");
app.setHeight('380');
app.setHeight('460');

var helpLabel = app
.createLabel("From here you will configure access to fitbit -- Just supply your own"
Expand Down Expand Up @@ -293,17 +330,34 @@ function renderFitbitConfigurationDialog() {
projectKey.setName("projectKey");
projectKey.setWidth("100%");
projectKey.setText(getProjectKey());

var sheetIdLabel = app.createLabel("Sheet ID:");
var sheetId = app.createTextBox();
sheetId.setName("sheetId");
sheetId.setWidth("100%");
sheet_id = getSheetId();
if (!sheet_id) {
try {
sheet_id = SpreadsheetApp.getActiveSpreadsheet().getId();
}
catch (e) {
Logger.log("Could not get the Sheet ID.");
}
}
sheetId.setText(sheet_id);

var saveHandler = app.createServerClickHandler("saveConfiguration");
var saveButton = app.createButton("Save Configuration", saveHandler);

var listPanel = app.createGrid(6, 3);
var listPanel = app.createGrid(7, 2);
listPanel.setWidget(1, 0, consumerKeyLabel);
listPanel.setWidget(1, 1, consumerKey);
listPanel.setWidget(2, 0, consumerSecretLabel);
listPanel.setWidget(2, 1, consumerSecret);
listPanel.setWidget(3, 0, projectKeyLabel);
listPanel.setWidget(3, 1, projectKey);
listPanel.setWidget(4, 0, sheetIdLabel);
listPanel.setWidget(4, 1, sheetId);

// add checkboxes to select loggables
var loggables = app.createListBox(true).setId("loggables").setName("loggables");
Expand All @@ -315,8 +369,8 @@ function renderFitbitConfigurationDialog() {
loggables.setItemSelected(parseInt(resource), true);
}
}
listPanel.setWidget(4, 0, app.createLabel("Resources:"));
listPanel.setWidget(4, 1, loggables);
listPanel.setWidget(5, 0, app.createLabel("Resources:"));
listPanel.setWidget(5, 1, loggables);

var period = app.createListBox(false).setId("period").setName("period");
period.setVisibleItemCount(1);
Expand All @@ -325,8 +379,8 @@ function renderFitbitConfigurationDialog() {
period.addItem(PERIODS[resource]);
}
period.setSelectedIndex(PERIODS.indexOf(getPeriod()));
listPanel.setWidget(5, 0, app.createLabel("Period:"));
listPanel.setWidget(5, 1, period);
listPanel.setWidget(6, 0, app.createLabel("Period:"));
listPanel.setWidget(6, 1, period);

// Ensure that all form fields get sent along to the handler
saveHandler.addCallbackElement(listPanel);
Expand Down Expand Up @@ -359,7 +413,7 @@ function getService() {
.setProjectKey(getProjectKey())
.setCallbackFunction('fitbitAuthCallback')
.setPropertyStore(PropertiesService.getScriptProperties())
.setScope('activity')
.setScope('heartrate activity')
.setTokenHeaders({
'Authorization': 'Basic ' + Utilities.base64Encode(getConsumerKey() + ':' + getConsumerSecret())
});
Expand Down Expand Up @@ -424,20 +478,24 @@ function onInstall() {
}

// Find the right row for a date.
function findRow(date) {
var doc = SpreadsheetApp.getActiveSpreadsheet();
function findRow(date, doc) {
var cell = doc.getRange("A3");

var column = doc.getRange('A:A');
var values = column.getValues(); // get all data in one call
var ct = 0;

// Find the first cell in first column which is either empty,
// or has an equal or bigger date than the one we are looking for.
while ((cell.getValue() != "") && (cell.getValue() < date)) {
cell = cell.offset(1,0);
while ( (values[ct][0] != "") && values[ct][0] < date ) {
ct++;
}
cell = cell.offset(ct, 0);

// If the cell we found has a newer date than ours, we need to
// insert a new row right before that.
if (cell.getValue() > date) {
doc.insertRowBefore(cell.getRow())
}
// return only the number of the row.
return (cell.getRow());
}
}
25 changes: 17 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
This little script runs in the Google App Script environment.
This little script runs in the Google App Script environment.

Specifically it runs in [Google Spreadsheets][0]. It lets you suck down your Fitbit data and the do all kinds of analysis. It's also an easy way to get started with the Fitbit API.
Specifically it runs in [Google Spreadsheets][0]. It lets you suck down your Fitbit data and the do all kinds of analysis. It's also an easy way to get started with the Fitbit API. This code is based on the work loghound did in [his project][3]. I have added support for heart rate figures that are produced by a Fitbit Charge HR. This version can also be scheduled using triggers.

If you want to know how OAuth 2.0 works, have a look at [a sample][2].

Sadly to get started is a bit of a pain:

1. Create a new Google Spreadsheet.
2. Go to Tools-->Script Editor
3. Replace the template with fitbit.js & reload the spreadsheet
4. From the Fitbit menu that should appear, run the Configure option
5. Follow all the instructions given in the form that pops up
6. Run the "Authorize" menu option -- this will run through the oauth dance.
7. Run the 'Refresh fitbit Time Data" menu option to get your data
8. Profit!
3. Replace the template with fitbit.js.
4. Save your changes and reload your sheet.
5. From the Fitbit menu in your Spreadsheet, run the Configure option. If you don't see the option, run "onInstall" function in script editor and try again.
6. Follow the instructions given in the form that pops up. You'll have to set up a Fitbit dev account.
7. Run the "Authorize" menu option -- this will run through the OAuth dance.
8. Run the 'Refresh fitbit Time Data" menu option to get your data
9. Profit!

Optional:
10. Set up a trigger for your script to run periodically.
- Script editor --> Resources --> All your triggers --> Add a new trigger for "refreshTimeSeries".

[0]: http://drive.google.com
[1]: https://github.com/loghound/Fitbit-for-Google-App-Script
[2]: https://github.com/googlesamples/apps-script-oauth2
[3]: https://github.com/loghound/Fitbit-for-Google-App-Script