Lifehacking with NFC, Tasker and HabitRPG
Oh man, I'm such a geek...
Being a single parent has required a considerable amount of self-discipline on my behalf. I find I do best in an environment with routine to make sure stuff gets done. One of the things I did to try and make sure stuff got done was to form positive habits. To help and make this a bit more "fun", I started using HabitRPG, an online role-playing game based on habits.
I've only ever used it in a half-arsed manner, and as a result, I die a lot. Often I'll do my dailies, but then forget to mark them off. Sometimes I just fall off the habit wagon altogether. And die.
One of my dailies is to scoop my cat's litterboxes. For whatever reason, I find this a tad onerous, and had found myself falling out of the habit of doing it on a daily basis. My cat deserves better than this, so I wanted to get back on the habit wagon, but make it a bit more "fun" as well.
I love NFC, I've been a big NFC weenie for a long time. It's a solution looking for a problem. I have a huge collection of NFC tags, and I've finally found a problem I could use them with. I wanted to make it more frictionless to mark a task as completed in HabitRPG. I didn't want to have to take out my phone, unlock it, open the HabitRPG app and check off the daily task. I just wanted to wave my phone at an NFC tag. Here's how I did it.
The inimitable Paul Fenwick, who first inspired me to use HabitRPG, has a way more complicated set up to achieve something similar. The Site Reliability Engineer in me wanted the least number of moving parts and third-parties to have to get from my phone to HabitRPG.
After some hunting around, I found this wiki page on integrating Tasker with HabitRPG's API, so based on that breadcrumb, I got hacking.
I'd not used Tasker before. I was already using AutomateIt to do some other, reasonably dumb automation on my phone, though. Tasker is a little bit obtuse, and it took me a couple of days of casual fiddling with it to wrap my head around it's usage model. The fact that it can run arbitrary JavaScript, and that NFC Tools integrates with it is where the real awesome lies.
So based off the wiki, I crafted a couple of bits of JavaScript, one to mark daily tasks as complete on HabitRPG, and one to query HabitRPG to see if they have been marked as complete.
For the former, it was trivial, using NFC Tools, to write an NFC tag that then runs the Tasker task to call the HabitRPG API to mark a daily task as complete. That goal was now complete.
The equally satisfying part was also having Tasker do a time-based conditional nag based on the state of the daily task in HabitRPG. So now if it gets to 4:45pm and I haven't scooped the litterboxes, my phone literally tells me I'd better go do it.
I've also done the same thing with putting the dishwasher on. I've stuck an NFC tag on the dishwashing powder bottle lid, and I get a conditional reminder before bed time. It used to be an unconditional reminder with AutomateIt, and it was dumb, because I rarely forget to put the dishwasher on. Now, I can use HabitRPG to keep state on it instead.
The hunk of JavaScript to update HabitRPG is very simple:
function mark_completed() { var http = new XMLHttpRequest(); http.open("POST", http_post_url, false); http.setRequestHeader("x-api-user", global('HabitrpgUserid')); http.setRequestHeader("x-api-key", global('HabitrpgApiToken')); http.send(); return(http.responseText); } try { var result = mark_completed(); } catch(e) { var error = e.message; }
All of the interesting stuff is defined in variables preceding the JavaScript in the task definition. Here's a screenshot of the Task in Tasker that tells a thousand words:
Similarly, to query HabitRPG, I'm using:
function query_task() { var http = new XMLHttpRequest(); http.open("GET",http_get_url,false); http.setRequestHeader("x-api-user", global('HabitrpgUserid')); http.setRequestHeader("x-api-key", global('HabitrpgApiToken')); http.send(); var p = JSON.parse(http.responseText); return p['completed']; } try { var task_completed = query_task(); } catch(e) { var error = e.message; }
It's only slightly trickier because you have to parse the JSON blob that comes back.
Again, a screenshot.
Now maybe I'll stop dying so much in HabitRPG, my cat will have the clean toilet she deserves, and I'll stop getting reminded about putting the dishwasher on when I already have. Better living through automation, that's my motto.