Mobile
Article

How to Make a Useful Toggl Time Tracker with Particle and Node

By Patrick Catanzariti

I have recently been tracking my time on projects more closely throughout the day. It is useful to see which projects take up more time than others and helps me measure which days I’m most productive (and what is distracting me!). My service of choice for this is Toggl. It is simple, clean and syncs across devices. Best of all – it has an API which you can hook up your own applications and devices to. I decided to set up a button connected to my Particle Photon that would start and stop my Toggl timer for me. I used a simple Node server to manage the communication between my Particle device and Toggl.

Clicking a physical button feels just that little bit more empowering than tapping a software button and prevents me needing to get out my smartphone or click around on my Mac to find the timer!

What You’ll Need

  • A Particle Core or Photon – I’ll be using a Particle Photon but both should be compatible with the demo
  • A physical button of some kind
  • A breadboard, resistors and jumper wires – If you are new to tinkering with microcontrollers, SparkFun has a great new Inventors Kit for the Photon
  • A Toggl account – If you don’t have one, head to the Toggl website to sign up!
  • Knowledge of how to get code onto your Particle device – if you’re new to this, I published a SitePoint article a few weeks back on connecting to the Photon. The Particle Core is similar.
  • A basic understanding of running a Node server and using npm – Peter Dierx at SitePoint has written a pretty comphrensive guide on starting with npm.

Note: Particle also sell a big physical button. You could quite likely adapt this concept to the big button for a lot of fun, I just don’t own one of those… yet.

Finding Your API Keys

To get your Toggl API key, visit the Toggl “My Profile” Page. If you scroll down to the bottom of this page, you’ll find a unique API token you can use like so:

Finding your Toggl API key

Copy that token to a safe place. You’ll need it!

You can also reset it using the tiny “Reset” link on the right hand side (useful in moments like just then when I revealed my API key to you all).

If it has been a while since your last Particle build session and you need a refresher on finding your Particle API key, go to the Particle Build online editor and click the gear icon at the very bottom to get to the settings page. From there, you’ll see a screen which shows you your access token.

Finding your Particle API key

Copy that one too.

Our Particle Sketch

Our sketch with the layout of the breadboard, Particle device (shown as a Core in this pic but both this and a Photon will work), LED and button looks like so:

Sketch of our Particle Toggl button

Download The Code

All of the code for this example can be found on GitHub.

Our Particle Code

Our Particle code will keep track of whether or not the button is pressed and whether or not we want to have our LED lit up or not. All the rest of the functionality will be taken care of by our Node server.

The Particle code looks like so:

int ledPin = D0;
int buttonPin = D5;
bool ready = true;
int last;

void setup() {
    pinMode(ledPin, OUTPUT);
    pinMode(buttonPin, INPUT);
    last = millis();
    digitalWrite(ledPin, LOW);
    
    Spark.function("ledTrigger", ledTrigger);
}

void loop() {
    if (millis() - last > 200) {
        if (digitalRead(buttonPin)) {
          if (ready) {
            ready = false;
            Spark.publish("buttonPressed");
            last = millis();
          }
        } else {
          ready = true; // button ready to be pressed again
        }
    }
}

int ledTrigger(String value) {
    if (value == "ON") {
      digitalWrite(ledPin, HIGH);
    } else {
        digitalWrite(ledPin, LOW);
    }
  
  return 0;
}

I’ll explain what each bit of that code means. To start with, we define our two components and the pins they are attached to. Our button is attached to D5 and our LED is attached to pin D0.

int ledPin = D0;
int buttonPin = D5;

The next two variables are there to keep track of timing within our loop. ready tracks whether our button is ready to be pressed again. We want to ensure there is a period between when we first click it and when it can be clicked again. last is the variable which helps track this period of time, it keeps track of the last time the loop has been run. That might make more sense when you see it in action soon.

bool ready = true;
int last;

In our setup() function, we start by setting the pin mode for our LED to output and set it to input for our button.

void setup() {
    pinMode(ledPin, OUTPUT);
    pinMode(buttonPin, INPUT);
    
    // more code explained next!
}

After that, we use the last variable I was explaining earlier. We initially set it to millis(). The millis() function returns the number of milliseconds since this program began running on our Particle device. This way, we know the time our setup() function began to run.

last = millis();

We set our LED to LOW initially, which turns it off.

digitalWrite(ledPin, LOW);

Lastly, in our setup() function, we define a public function that our Node code will be able to access. This function is our ledTrigger() function, as we want our Node code to be able to turn the LED on and off.

Spark.function("ledTrigger", ledTrigger);

In our loop() function, we run code every 200 milliseconds. We work that out by reading the current value of millis() and taking it from our last value. There are other alternative methods to check for button presses but this was one shown by the Particle team and also the only one that seems to work for me!

void loop() {
    if (millis() - last > 200) {
      // Our checks for button presses
    }
}

Each 200 milliseconds, we check if there is a signal from our button. If we are seeing a signal from our button and our ready variable is true, then we publish out a "buttonPressed" event for our Node server to hear. We also set ready to false so the button won’t press multiple times and last to this current millis() (so we can now wait another 200 milliseconds before running again).

if (digitalRead(buttonPin)) {
  if (ready) {
    ready = false;
    Spark.publish("buttonPressed");
    last = millis();
  }
}

If there is no signal from our button after the 200 milliseconds, we set ready to true, as our button has been released and thus we can start watching for the next 200 millisecond interval when it is pressed again.

else {
  ready = true; // button ready to be pressed again
}

The last bit of code is our public ledTrigger() function we mentioned earlier. This is what our Node code will use to turn our LED on and off. Each function call is also passed a string of either "ON" or "OFF". If we see "ON" passed into the function, we set our LED to HIGH. Otherwise, we set it to LOW turning it off.

int ledTrigger(String value) {
    if (value == "ON") {
      digitalWrite(ledPin, HIGH);
    } else {
        digitalWrite(ledPin, LOW);
    }
  
  return 0;
}

Our Node Server

Within our Node server, we take care of all the integration between our Toggl account and our Particle device. Our Node server looks like so:

var spark = require("spark"),
  TogglClient = require("toggl-api"),
  toggl = new TogglClient({apiToken: "YOURAPITOKEN"}),
  _ = require("underscore"),
  currentParticle;

initParticle();

function initParticle() {
  spark.on("login", function(err, body) {
    console.log("Particle Core login successful: ", body);
    var deviceList = spark.listDevices();

    deviceList.then(function(devices) {
      currentParticle = _.find(devices, function(device) {
        return device.name == "Timon";
      });
      
      console.log("Timon was found: ", currentParticle);

      currentParticle.onEvent("buttonPressed", function() {
        console.log("Button was pressed!");

        toggl.getCurrentTimeEntry(function(err, currentEntry) {
          if (currentEntry) {
            console.log(currentEntry.description + " is running");

            toggl.stopTimeEntry(currentEntry.id, function(err, stoppedEntry) {
              console.log(stoppedEntry.description + " was stopped");

              currentParticle.callFunction("ledTrigger", "OFF", function(result) {
                console.log("LED should be off");
              });
            });
          } else {
            var currentDate = new Date(),
                yesterday = new Date();

            yesterday.setDate(currentDate.getDate() - 1);
            
            toggl.getTimeEntries(yesterday.toISOString(), currentDate.toISOString(), function(err, data) {
              if (!err) {
                var lastEntry = data[data.length - 1];
                console.log(lastEntry);

                toggl.startTimeEntry({
                  description: lastEntry.description,
                  pid: lastEntry.pid,
                  wid: lastEntry.wid
                }, function(err, timeEntry) {
                  console.log("Entry started");

                  currentParticle.callFunction("ledTrigger", "ON", function(result) {
                    console.log("LED should be on");
                  });
                });
              }
            });
          }
        });
      });
    });
  });

  spark.login({
    accessToken: "YOURACCESSTOKEN"
  }, function(err, body) {
    if (!err) console.log("API login complete!");
  });
}

It begins with us requiring the spark, toggl-api and underscore npm libraries. spark is the library we use to access the Particle Core (it used to be called a “Spark Core”), toggl-api is a nice and simple library that lets us access our Toggl API, we just need to pass in our API token as you see below. underscore will be used to make it simpler to look through the data that is returned from the Toggl API. currentParticle is where we’ll store the details of the Particle Core we’re using.

var spark = require("spark"),
  TogglClient = require("toggl-api"),
  toggl = new TogglClient({apiToken: "YOURAPITOKEN"}),
  _ = require("underscore"),
  currentParticle;

Everything happens within a function we’ve got called initParticle(). We initially log into the Particle service using our access token and then use our access within the spark.on("login") event:

function initParticle() {
  spark.on("login", function(err, body) {
    console.log("Particle device login successful: ", body);
    
    // We'll be accessing our Particle here
  });

  spark.login({
    accessToken: "YOURACCESSTOKEN"
  }, function(err, body) {
    if (!err) console.log("API login complete!");
  });
}

Within our logged in state, we then use spark.listDevices() to get a list of all devices attached to that account. Once that has been returned, we use the underscore library to search through the results and find the particular Particle device we have our button attached to. I named my Particle device “Timon”, so I look for that name below. Once we find the device, we attach it to the currentParticle.

var deviceList = spark.listDevices();

deviceList.then(function(devices) {
  currentParticle = _.find(devices, function(device) {
    return device.name == "Timon";
  });
  
  console.log("Timon was found: ", currentParticle);

Once we have our Particle device, we watch for the "buttonPressed" event that we set our Particle device to emit whenever it finds our button was pushed down. If we see that event, we respond:

currentParticle.onEvent("buttonPressed", function() {
  console.log("Button was pressed!");

  // We'll talk to Toggl here next!
});

We have reached the point in our code where we need to speak to Toggl to let them know that we want to do something with our time tracking. We first want to know whether there is a project already being tracked. We can do that with toggl.getCurrentTimeEntry(). If there is a current time entry, it is returned within the currentEntry variable. We check for that variable and if there’s already an entry running, we want our button press to stop that timer. We do that via the toggl.stopTimeEntry() function. When that function is successful, we tell our Particle device to turn our LED off via the currentParticle.callFunction("ledTrigger") call you can see below.

toggl.getCurrentTimeEntry(function(err, currentEntry) {
  if (currentEntry) {
    console.log(currentEntry.description + " is running");

    toggl.stopTimeEntry(currentEntry.id, function(err, stoppedEntry) {
      console.log(stoppedEntry.description + " was stopped");

      currentParticle.callFunction("ledTrigger", "OFF", function(result) {
        console.log("LED should be off");
      });
    });
  }
  // We will have an else statement next!
}

If there isn’t a current event running, we instead want to find the last event that ran and resume it via the button press (as we can’t really define an event within a button press, we’ll assume we are restarting our last event that we defined within the desktop or mobile apps).

To find past events, we use the toggl.getTimeEntries() function. This function takes two variables, the start date and end date we want to look for events within. We only really want the very last event, so we set it to look through the past day. We set up two variables for this, currentDate which is the typical new Date() in JavaScript, and yesterday which is our currentDate minus one. These need to be in ISO 8601 date time format for them to work with Toggl, so we convert them both using toISOString().

else {
  var currentDate = new Date(),
      yesterday = new Date();

  yesterday.setDate(currentDate.getDate() - 1);
  
  toggl.getTimeEntries(yesterday.toISOString(), currentDate.toISOString(), function(err, data) {
    if (!err) {
      // We have a time entry to begin!
    }
  });
}

If we have no errors returned, we’ll have an array called data which contains our Toggl time entries of the last day. To get the very last one we’ve logged, we get the last element in the array via data[data.length - 1] and assign it to lastEntry.

var lastEntry = data[data.length - 1];
console.log(lastEntry);

Now we know our last entry and can start a new entry of the same project and task. To start a new time entry, we run the toggl.startTimeEntry() function. We pass in the description (the name of your entry in Toggl), pid (the project ID) and wid (the workspace ID) of our lastEntry, so that it starts the same task and assigns it to the same project. When it successfully starts our time tracking, we call our "ledTrigger" function again, this time turning our LED on to show we’re tracking a project via the Particle device.

toggl.startTimeEntry({
  description: lastEntry.description,
  pid: lastEntry.pid,
  wid: lastEntry.wid
}, function(err, timeEntry) {
  console.log("Entry started");

  currentParticle.callFunction("ledTrigger", "ON", function(result) {
    console.log("LED should be on");
  });
});

In Action

Put this code onto your Particle device and run the usual npm install and node index.js to get your Node server running.

You should now be able to click the button to start and stop your Toggl tracking! One thing I noticed is that my Mac Toggl app doesn’t pick up time entries straight away if they weren’t initially run from the app. However, my Android Toggl app syncs much faster and had an almost real time response to my button presses, showing a new time entry starting and stopping.

Here’s a video of mine in action:

For those who don’t want to watch video, here’s a picture the moment before the button is pressed, my Toggl tracking is ready and waiting:

Particle Toggl Button about to be clicked

Once it registers the click, Toggl begins tracking the last task and project I’d previously been working on. The LED also turns on to show the Particle device has successfully started tracking something:

Particle Toggl Button clicked and tracking

Click the button once more when you are ready to stop the project tracking!

Conclusion

With this knowledge you can go out and build your own IoT connected Toggl applications. Connect it up to absolutely anything to track your productivity, use your imagination and see what brilliant ideas come into your mind! There is definitely more that could be done with this demo. One area that definitely warrants more work is syncing the LED with Toggl when tracking is begun on other devices.

If you do make a really interesting and unique Toggl time tracker app based upon this code, please share it in the comments or get in touch with me on Twitter (@thatpatrickguy). I’d love to see it!

If you’re looking for more links and sample projects to guide you in your Particle development, I’ve got a set of curated links that might help! Head over to Dev Diner and check out my Dev Diner Particle Developer Guide.

  • http://careersreport.com amy Place

    I want to show this great internet job opportunity… 3 to 5 hrs of work /a day… Once a week payment… Bonus opportunities…Payscale of 6-9 thousand dollars /a month… Merely several h of your! free time, desktop or laptop, elementary knowledge of www and reliable internet-connection is what is needed…Have a visit to my disqus_profile for more info

  • Chocanto

    Nice work ! :)

    • Patrick Catanzariti

      Cheers :)

  • Felipe Alvarado S

    Gosh thats so cool I cannot even!

    • Patrick Catanzariti

      Thanks!

  • Fernando Pintado

    Pretty cool, would be nice if we can watch a demo video! Thanks for the article

    • Patrick Catanzariti

      Funny you say that! I have a demo video planned, will update the article with it once I’ve got one ready to go :)

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Mobile, once a week, for free.