Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // intraday.gs -- Supports downloading intraday (minute-by-minute) step data
- // Will not work without special permission from Fitbit, with the exception that you can use it for your own personal data if your app type is set to personal.
- /*
- Script modified June 2016 to act as an SMS gateway idle alert.
- All previous comments and acknowledgments left intact so that I myself properly acknowledge sources!!
- Original script found here: https://github.com/simonbromberg/googlefitbit/blob/master/intraday.gs
- */
- // Simon Bromberg (http://sbromberg.com)
- // You are free to use, modify, copy any of the code in this script for your own purposes, as long as it's not for evil
- // If you do anything cool with it, let me know!
- // Note: there are minor improvements/cleanups still to be made in this file, but it should work as is if everything is setup properly
- // See readme on github repo for more information
- // Script based on post here http://quantifiedself.com/2014/09/download-minute-fitbit-data/ by Ernesto Ramirez
- /*
- * Do not change these key names. These are just keys to access these properties once you set them up by running the Setup function from the Fitbit menu
- */
- // Key of ScriptProperty for Fitbit consumer key.
- var CONSUMER_KEY_PROPERTY_NAME = "fitbitConsumerKey";
- // Key of ScriptProperty for Fitbit consumer secret.
- var CONSUMER_SECRET_PROPERTY_NAME = "fitbitConsumerSecret";
- var SERVICE_IDENTIFIER = 'fitbit';
- // This variable assumes that you are as lazy as I am and left your sheet generically named 'Sheet1'. If not, change as appropriate.
- var DOC = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
- /* These 2 variables tell the script how many minutes in the past you want to use for your count, and how many steps to look for.
- I set my script to run every 15 minutes and look for 125 steps within the past 30. In theory, this will always ensure that I
- make my 250 steps per hour goal. */
- var MINUTES_TO_SUM = 30;
- var STEPS_TARGET = 125;
- /*These 2 variables set the active timeframe in which the script is allowed to send 'nag' messages, based on 24 hour time (0-23). I have my fitbit set to look
- for 250 steps each hour from 7:00 AM until 8:00 PM. Thus 7 and 20 below. Adjust as appropriate for your application. */
- var FIRST_HOUR_TO_NAG = 7;
- var LAST_HOUR_TO_NAG = 20;
- function onOpen() {
- var ss = SpreadsheetApp.getActiveSpreadsheet();
- var menuEntries = [
- {
- name: "Setup",
- functionName: "setup"
- },
- {
- name: "Authorize",
- functionName: "showSidebar"
- },
- {
- name: "Reset",
- functionName: "clearService"
- },
- {
- name: "Download data",
- functionName: "refreshTimeSeries"
- }];
- ss.addMenu("Fitbit", menuEntries);
- }
- function isConfigured() {
- return getConsumerKey() != "" && getConsumerSecret() != "";
- }
- function setConsumerKey(key) {
- ScriptProperties.setProperty(CONSUMER_KEY_PROPERTY_NAME, key);
- }
- function getConsumerKey() {
- var key = ScriptProperties.getProperty(CONSUMER_KEY_PROPERTY_NAME);
- if (key == null) {
- key = "";
- }
- return key;
- }
- function setLoggables(loggable) {
- ScriptProperties.setProperty("loggables", loggable);
- }
- function getLoggables() {
- var loggable = ScriptProperties.getProperty("loggables");
- if (loggable == null) {
- loggable = LOGGABLES;
- } else {
- loggable = loggable.split(',');
- }
- return loggable;
- }
- function setConsumerSecret(secret) {
- ScriptProperties.setProperty(CONSUMER_SECRET_PROPERTY_NAME, secret);
- }
- function getConsumerSecret() {
- var secret = ScriptProperties.getProperty(CONSUMER_SECRET_PROPERTY_NAME);
- if (secret == null) {
- secret = "";
- }
- return secret;
- }
- // function saveSetup saves the setup params from the UI
- function saveSetup(e) {
- setConsumerKey(e.parameter.consumerKey);
- setConsumerSecret(e.parameter.consumerSecret);
- setLoggables(e.parameter.loggables);
- setFirstDate(e.parameter.firstDate);
- setLastDate(e.parameter.lastDate);
- var app = UiApp.getActiveApplication();
- app.close();
- return app;
- }
- /* Since I have repurposed this script solely as an idle alert, I now have it set to clear the sheet every time it's run. */
- function clearSheet() {
- DOC.getRange('A1:D' + DOC.getLastRow()).clearContent();
- }
- /* The inbuilt date formatting utility seemed to have taken an unscheduled leave of absence when I attempted to used this script as originally written.
- As such, I copied this function from Stackoverflow to accomplish my goal.*/
- function getToday() {
- var today = new Date();
- var dd = today.getDate();
- var mm = today.getMonth() + 1; //January is 0!
- var yyyy = today.getFullYear();
- if(dd<10) {
- dd='0'+dd
- }
- if(mm<10) {
- mm='0'+mm
- }
- today = yyyy + '-' + mm + '-' + dd;
- return today;
- }
- function setFirstDate(firstDate) {
- ScriptProperties.setProperty("firstDate", firstDate);
- }
- function getFirstDate() {
- var firstDate = ScriptProperties.getProperty("firstDate");
- if (firstDate == null) {
- firstDate = getToday();
- }
- }
- function setLastDate(lastDate) {
- ScriptProperties.setProperty("lastDate", lastDate);
- }
- function getLastDate() {
- var lastDate = ScriptProperties.getProperty("lastDate");
- if (lastDate == null) {
- var lastDate = getToday();
- }
- return lastDate;
- }
- // function setup accepts and stores the Consumer Key, Consumer Secret, Project Key, firstDate, and list of Data Elements
- function setup() {
- var doc = SpreadsheetApp.getActiveSpreadsheet();
- var app = UiApp.createApplication().setTitle("Setup Fitbit Download");
- app.setStyleAttribute("padding", "10px");
- var consumerKeyLabel = app.createLabel("Fitbit OAuth 2.0 Client ID:*");
- var consumerKey = app.createTextBox();
- consumerKey.setName("consumerKey");
- consumerKey.setWidth("100%");
- consumerKey.setText(getConsumerKey());
- var consumerSecretLabel = app.createLabel("Fitbit OAuth Consumer Secret:*");
- var consumerSecret = app.createTextBox();
- consumerSecret.setName("consumerSecret");
- consumerSecret.setWidth("100%");
- consumerSecret.setText(getConsumerSecret());
- var projectKeyTitleLabel = app.createLabel("Project key: ");
- var projectKeyLabel = app.createLabel(ScriptApp.getProjectKey());
- var firstDate = app.createTextBox().setId("firstDate").setName("firstDate");
- firstDate.setName("firstDate");
- firstDate.setWidth("100%");
- firstDate.setText(getFirstDate());
- var lastDate = app.createTextBox().setId("lastDate").setName("lastDate");
- lastDate.setName("lastDate");
- lastDate.setWidth("100%");
- lastDate.setText(getLastDate());
- // create the save handler and button
- var saveHandler = app.createServerClickHandler("saveSetup");
- var saveButton = app.createButton("Save Setup", saveHandler);
- // put the controls in a grid
- var listPanel = app.createGrid(8, 3);
- listPanel.setWidget(1, 0, consumerKeyLabel);
- listPanel.setWidget(1, 1, consumerKey);
- listPanel.setWidget(2, 0, consumerSecretLabel);
- listPanel.setWidget(2, 1, consumerSecret);
- listPanel.setWidget(3, 0, app.createLabel(" * (obtain these at dev.fitbit.com, use OAuth2.0)"));
- listPanel.setWidget(4, 0, projectKeyTitleLabel);
- listPanel.setWidget(4, 1, projectKeyLabel);
- listPanel.setWidget(5, 0, app.createLabel("Start Date for download (yyyy-mm-dd)"));
- listPanel.setWidget(5, 1, firstDate);
- listPanel.setWidget(6, 0, app.createLabel("End date for download (yyyy-mm-dd)"));
- listPanel.setWidget(6, 1, lastDate);
- listPanel.setWidget(7, 0, app.createLabel("Very long intervals will not work; exceed Fitbit rate limit and/or function will timeout"));
- // Ensure that all controls in the grid are handled
- saveHandler.addCallbackElement(listPanel);
- // Build a FlowPanel, adding the grid and the save button
- var dialogPanel = app.createFlowPanel();
- dialogPanel.add(listPanel);
- dialogPanel.add(saveButton);
- app.add(dialogPanel);
- doc.show(app);
- }
- function getFitbitService() {
- // Create a new service with the given name. The name will be used when
- // persisting the authorized token, so ensure it is unique within the
- // scope of the property store
- Logger.log(PropertiesService.getUserProperties());
- return OAuth2.createService(SERVICE_IDENTIFIER)
- // Set the endpoint URLs, which are the same for all Google services.
- .setAuthorizationBaseUrl('https://www.fitbit.com/oauth2/authorize')
- .setTokenUrl('https://api.fitbit.com/oauth2/token')
- // Set the client ID and secret, from the Google Developers Console.
- .setClientId(getConsumerKey())
- .setClientSecret(getConsumerSecret())
- // Set the name of the callback function in the script referenced
- // above that should be invoked to complete the OAuth flow.
- .setCallbackFunction('authCallback')
- // Set the property store where authorized tokens should be persisted.
- .setPropertyStore(PropertiesService.getUserProperties())
- .setScope('activity profile')
- .setTokenHeaders({
- 'Authorization': 'Basic ' + Utilities.base64Encode(getConsumerKey() + ':' + getConsumerSecret())
- });
- }
- function clearService(){
- OAuth2.createService(SERVICE_IDENTIFIER)
- .setPropertyStore(PropertiesService.getUserProperties())
- .reset();
- }
- function showSidebar() {
- var service = getFitbitService();
- if (!service.hasAccess()) {
- var authorizationUrl = service.getAuthorizationUrl();
- var template = HtmlService.createTemplate(
- '<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' +
- 'Reopen the sidebar when the authorization is complete.');
- template.authorizationUrl = authorizationUrl;
- var page = template.evaluate();
- SpreadsheetApp.getUi().showSidebar(page);
- } else {
- Logger.log("Has access!!!!");
- }
- }
- function authCallback(request) {
- Logger.log("authcallback");
- var service = getFitbitService();
- var isAuthorized = service.handleCallback(request);
- if (isAuthorized) {
- Logger.log("success");
- return HtmlService.createHtmlOutput('Success! You can close this tab.');
- } else {
- Logger.log("denied");
- return HtmlService.createHtmlOutput('Denied. You can close this tab');
- }
- }
- function getUser() {
- var service = getFitbitService();
- var options = {
- headers: {
- "Authorization": "Bearer " + service.getAccessToken(),
- "method": "GET"
- }};
- var response = UrlFetchApp.fetch("https://api.fitbit.com/1/user/-/profile.json",options);
- var o = JSON.parse(response.getContentText());
- return o.user;
- }
- /* Here we are using a formula within the spreadsheet to calculate our steps during the time frame we set above.
- If the number of steps does not match or exceed out set target the 'nag function is called. */
- function calculateSteps() {
- var cell = DOC.getRange('C' + DOC.getLastRow());
- var startofrange = DOC.getLastRow() - MINUTES_TO_SUM;
- cell.setFormula("=SUM(B" + startofrange + ":B" + DOC.getLastRow() + ")");
- var numberofsteps = cell.getValue();
- if (numberofsteps < STEPS_TARGET) {
- nag(numberofsteps);
- }
- }
- function refreshTimeSeries() {
- if (!isConfigured()) {
- setup();
- return;
- }
- clearSheet();
- var user = getUser();
- var doc = DOC;
- doc.setFrozenRows(2);
- // two header rows
- doc.getRange("a1").setValue(user.fullName);
- doc.getRange("a1").setComment("DOB:" + user.dateOfBirth)
- doc.getRange("b1").setValue(user.country + "/" + user.state + "/" + user.city);
- var options =
- {headers:{
- "Authorization": 'Bearer ' + getFitbitService().getAccessToken(),
- "method": "GET"
- }};
- var activities = ["activities/log/steps"];
- var intradays = ["activities-log-steps-intraday"];
- var lastIndex = 0;
- for (var activity in activities) {
- var index = 0;
- var dateString = getToday();
- date = parseDate(dateString);
- var table = new Array();
- while (1) {
- var currentActivity = activities[activity];
- try {
- var result = UrlFetchApp.fetch("https://api.fitbit.com/1/user/-/" + currentActivity + "/date/" + dateString + "/1d" + ".json", options);
- } catch(exception) {
- Logger.log(exception);
- }
- var o = JSON.parse(result.getContentText());
- var cell = doc.getRange('a3');
- var titleCell = doc.getRange("a2");
- titleCell.setValue("Date");
- var title = currentActivity.split("/");
- title = title[title.length - 1];
- titleCell.offset(0, 1 + activity * 1.0).setValue(title);
- var row = o[intradays[activity]]["dataset"];
- for (var j in row) {
- var val = row[j];
- var arr = new Array(2);
- arr[0] = dateString + ' ' + val["time"];
- arr[1] = val["value"];
- table.push(arr);
- // set the value index index
- index++;
- }
- date.setDate(date.getDate()+1);
- dateString = Utilities.formatDate(date, "GMT", "yyyy-MM-dd");
- if (dateString > getLastDate()) {
- break;
- }
- }
- // Batch set values of table, much faster than doing each time per loop run, this wouldn't work as is if there were multiple activities being listed
- doc.getRange("A3:B"+(table.length+2)).setValues(table);
- calculateSteps();
- }
- }
- // parse a date in yyyy-mm-dd format
- function parseDate(input) {
- var parts = input.match(/(\d+)/g);
- // new Date(year, month [, date [, hours[, minutes[, seconds[, ms]]]]])
- return new Date(parts[0], parts[1]-1, parts[2]); // months are 0-based
- }
- // parse a date in 2011-10-25T23:57:00.000 format
- function parseDate2(input) {
- var parts = input.match(/(\d+)/g);
- return new Date(parts[0], parts[1]-1, parts[2], parts[3], parts[4]);
- }
- /* Our nag function (AKA idle alert) starts by making sure it's within the time range that it's allowed to run (I won't get my steps in during the night
- unless I'm peeing a lot or sleepwalking!). The outgoing message is then set based on variables from above (modify as you see fit) and sent via gmail.*/
- function nag(stepcount) {
- var hour = new Date().getHours();
- if (hour >= FIRST_HOUR_TO_NAG && hour < LAST_HOUR_TO_NAG) {
- message = "MOVE IT!! You've only taken " + stepcount + " of " + STEPS_TARGET + " steps in the last " + MINUTES_TO_SUM + " minutes!!"
- /* I used information from http://stackoverflow.com/a/30053105 in order to send myself SMS that I could receive via Hangouts
- Obviously, you could send the email to any phone carrier's SMS or MMS gateway for a text to any carrier, OR just send yourself
- an email.*/
- }
- }
Add Comment
Please, Sign In to add comment