A Quick and Dirty Guide to Getting Started with the Pinoccio API.

In case I haven't told you, Pinoccio is an Ardunio-esque micro-controller board, that ships 'with wings': Each board has built-in mesh networking, and the kit comes with a Wifi board.

What's particularly unique about Pinoccio is that they're not leaving it just at that. They're providing a simple scripting language (based on the phenomenally cool Bitlash project), as well as an associated web-service and API provider (called HQ).

In Pinoccio parlance, a single board is called scout and a group of scouts connected by a mesh are called a troop. Cute eh?

Today, in the spirit of taking time off from thesis writing, and getting my head away from Transactional Memory, I figured I'd spend some time digging into the API they provide. I thought I'd write up some example code here, from my perspective (a relative novice to web-services entirely).

We're going to create two scripts: issue_command.py and sync_stream.py. The first will allow issuing a scout-script command through the API, and the second, will allow you to watch the API's sync stream, which watches all the events which hit HQ.

Before those though, I'll show you api_explorer.py which will allow exploring the read only elements of the API.

Authentication:

First step is to login and get an authenticated token, which is a string the API gives you as a temporary password which you attach to all your subsequent requests. This ensures that you, and only you, can control your Pinoccio boards.

For the purposes of this blog post, I'm going to skip the right way to do this, in order to get up and running faster, by showing you the dirty way, straight from the Pinoccio API Documentation.

On the command line, you can ask the API for a token using curl:

curl -X POST -v --data '{"email":"youremail","password":"boats"}' https://api.pinocc.io/v1/login

Take note of the reply:

{"data":{"token":"ahv0onrd0fiio8813llqjo5sb1","account":19}}

You'll need the token shortly.

HTTP Requests

I've been quite impressed with the python library Requests, which makes it reasonably easy to communicate with a service, bidirectionally.

You can install it, if you don't already have it, with pip:

pip install requests

api_explorer.py

While you could do this directly with curl, it's instructive to see how requests are constructed before going on further. To that end: here's api_explorer.py

import requests
import sys

API_Root="https://api.pinocc.io/v1/"
token = "your token here!"
auth_arg = {"token" : token} 

def get(url, **kwargs): 
    """
    Issue a get request to the Pinnocio API, authenticated by the token
    """
    if "data" in kwargs: 
        kwargs["data"] = dict(kwargs["data"].items() + auth_arg.items())
    else:
        kwargs["data"] = auth_arg
    return requests.get(API_Root + url, **kwargs)


if __name__  == '__main__': 
    if len(sys.argv) > 1: 
        r = get(sys.argv[1])
        print r.status_code
        print r.text
    else:
        print "Need a command" 

This simple script allows you to provide an endpoint on the command line, and print out the API's response. The function get above constructs the appropriate URL given the endpoint name, and makes sure that we have the token on the request. The kwargs is so that we can provide arguments to the call, which we will need soon...

troops is an interesting endpoint, as it will list the troops associated with the account you used to generate the token.

SolidALumbook:matt_pinoccio mgaudet$ python api_explore.py troops
200
{"data":[{"account":1112,"token":"<token data-preserve-html-node="true" elided>","name":"Scooters","id":1,"online":true}]}

Reading this JSON output, you can see I have one troop, "Scooters", with id 1. So, we can now ask the API about the scouts in that troop:

SolidALumbook:matt_pinoccio mgaudet$ python api_explore.py 1/scouts
200
{"data":[{"name":"Razer","updated":"1397008385993","id":1,"time":1397008385992},{"name":"Rascal","updated":"1397008855488","id":2,"time":1397008855488}]}

So I have two scouts in the troop: Razer (id 1) and Rascal (id 2).

These ID's are useful when talking about issuing commands.

issue_command.py

According to the API documents, to issue a ScoutScript command to a scout, you need to know its troop id and scout id. After which, you can issue a command with HTTP GET on the end point {troop id}/{scout id}/command. Using api_explorer.py we found out that Razer is scout 1 in troop 1. So if we want to issue a command to Razer, we can make a request to the 1/1/command endpoint.

Here's a script which allows you to specify a troop id, scout id, and a ScoutScript command. As you can see it's largely identical to api_explorer.py, except that "command" is provided as an argument to the get-request.

import requests
import sys

API_Root="https://api.pinocc.io/v1/"
token = "your token here"
auth_arg = {"token" : token} 

def get_token(username, password): 
    pass

def get(url, **kwargs): 
    if "data" in kwargs: 
        kwargs["data"] = dict(kwargs["data"].items() + auth_arg.items())
    else:
        kwargs["data"] = auth_arg
    return requests.get(API_Root + url, **kwargs)

if __name__  == '__main__': 
    if len(sys.argv) > 3: 
        command = " ".join(sys.argv[3:])
        r = get("{}/{}/command/".format(sys.argv[1],sys.argv[2]),data={"command": command}) 
        print r.status_code
        print r.text
    else:
        print "Need a troop, a scout, and a command" 

So we can turn on the led of Razer:

SolidALumbook:matt_pinoccio mgaudet$ python issue_command.py 1 1 led.on
200
{"data":{"type":"reply","from":1,"id":"t44947","end":true,"reply":"","_cid":13,"basetime":469,"commandsPending":0,"messagesQueued":0,"account":"1112","tid":"1","_t":1397421971331,"output":"","result":false}}

Or peek at the wifi status:

SolidALumbook:matt_pinoccio mgaudet$ python issue_command.py 1 1 "wifi.report()"
200
{"data":{"type":"reply","from":1,"id":"t44948","end":true,"reply":"{\"type\":\"wifi\",\"connected\":true,\"hq\":true}\r\n","_cid":14,"basetime":400,"commandsPending":0,"messagesQueued":0,"account":"1112","tid":"1","_t":1397421998605,"output":"{\"type\":\"wifi\",\"connected\":true,\"hq\":true}\r\n","result":false}}

sync_stream.py

One of the interesting elements of the Pinnocio API is that the scouts themselves will autonomously report information back to HQ. This data can be accessed through the API from the sync endpoint.

Here's a simple script which starts listening to the sync stream until killed:

import requests
import sys
import json

API_Root="https://api.pinocc.io/v1/"
token = "your API key here!"
auth_arg = {"token" : token} 

def get_token(username, password): 
    pass

def get(url, **kwargs): 
    if "data" in kwargs: 
        kwargs["data"] = dict(kwargs["data"].items() + auth_arg.items())
    else:
        kwargs["data"] = auth_arg
    return requests.get(API_Root + url, **kwargs)

if __name__  == '__main__': 
    received = 0
    r = get("sync",stream=True)
    print r.status_code

    # Chunk size required to avoid buffering output too long!
    for line in r.iter_lines(chunk_size=1): 
        received = received + 1 
        if line: 
            print "({}) {}".format(received, json.loads(line))
        else:
            print "({}) keepalive".format(received) 

Here's what it looks like in action:

SolidALumbook:matt_pinoccio mgaudet$ python sync_stream.py 
200
(1) {u'data': {u'account': u'1112', u'type': u'connection', u'troop': u'1', u'value': {u'status': u'online', u'ip': u'<ip data-preserve-html-node="true" elided>'}, u'time': 1397420589928}}
(2) {u'data': {u'account': u'1112', u'value': {u'state': [-1, -1, -1, -1, -1, -1, -1, -1], u'_t': 1397420590333.001, u'type': u'analog', u'mode': [-1, -1, -1, -1, -1, -1, -1, -1]}, u'scout': u'1', u'troop': u'1', u'time': 1397420590333.001, u'type': u'analog'}}
(3) {u'data': {u'account': u'1112', u'value': {u'available': 1, u'_t': 1397420590149.001, u'scout': 1, u'type': u'available'}, u'scout': u'1', u'troop': u'1', u'time': 1397420590149.001, u'type': u'available'}}
(4) {u'data': {u'account': u'1112', u'value': {u'available': 1, u'_t': 1397421532918.001, u'scout': 2, u'type': u'available'}, u'scout': u'2', u'troop': u'1', u'time': 1397421532918.001, u'type': u'available'}}
(5) {u'data': {u'account': u'1112', u'value': {u'list': [], u'_t': 1397420590332.001, u'type': u'backpacks'}, u'scout': u'1', u'troop': u'1', u'time': 1397420590332.001, u'type': u'backpacks'}}
(6) {u'data': {u'account': u'1112', u'value': {u'state': [-1, -1, -1, -1, -1, -1, -1], u'_t': 1397420590333, u'type': u'digital', u'mode': [-1, -1, -1, -1, -2, -2, -2]}, u'scout': u'1', u'troop': u'1', u'time': 1397420590333, u'type': u'digital'}}
(7) {u'data': {u'account': u'1112', u'value': {u'led': [0, 255, 0], u'torch': [0, 255, 0], u'type': u'led', u'_t': 1397421971069}, u'scout': u'1', u'troop': u'1', u'time': 1397421971069, u'type': u'led'}}
(8) {u'data': {u'account': u'1112', u'value': {u'led': [0, 34, 255], u'torch': [0, 34, 255], u'type': u'led', u'_t': 1397421537370}, u'scout': u'2', u'troop': u'1', u'time': 1397421537370, u'type': u'led'}}
(9) {u'data': {u'account': u'1112', u'value': {u'scoutid': 1, u'power': u'3.5 dBm', u'troopid': 1, u'_t': 1397420590334, u'rate': u'250 kb/s', u'routes': 0, u'type': u'mesh', u'channel': 20}, u'scout': u'1', u'troop': u'1', u'time': 1397420590334, u'type': u'mesh'}}
(10) {u'data': {u'account': u'1112', u'value': {u'vcc': True, u'battery': 73, u'_t': 1397422079321, u'voltage': 392, u'type': u'power', u'charging': False}, u'scout': u'1', u'troop': u'1', u'time': 1397422079321, u'type': u'power'}}

Download code:

All this code has been uploaded to github here, as PinoccioApiTools.

Conclusion

Pinoccio is providing some very powerful tools for building some interesting projects, minimizing a lot of pain points that previous solutions suffered from. I'm really excited by this.

What Caught My Eye - Week 12

Technology & Tech Culture:

  • This video featuring Monty Montgomery from Xiph is a phenomenal and cogent explanation of how analog to digital to analog conversion works, and why the stair-step chart we often see is a horrible lie.
  • Hiring is a bit of voodoo, and often times I hear people talk about Passion, which is a bit of a bugbear of mine. This post talks about how excellent candidates can be overlooked because the signifiers of 'Passion' aren't there.

Arts and Culture

Examining "Reproducibility in Computer Science"

I'm heartened to see Professor Shriram Krishnamurthi spearheading an effort to help evaluate yesterday's reproducibility project.

We are evaluating the results presented by the study at the University of Arizona. Our goal is to allow authors and any other interested parties to review that study’s findings about an individual paper, and attempt to reconstruct their findings. We will summarize the results here.
We are grateful to Collberg, et al. for initiating this discussion and making all their data available. This is a valuable service based on an enormous amount of manual labor. Even if we end up disagreeing with some of their findings, we remain deeply appreciative of their service to the community by highlighting these important issues.
We do wish disagree with the use of the term “reproducibility”, which many people associate with an independent reconstruction of the work. For instance, this paper spells out the difference between repeatability and reproducibility and provides an interesting case study.

I've already submit a dispute, where code isn't available where it was thought that it was. I plan to do a little more looking, time permitting.

Others have successfuly built projects reported as failures. I think that the community is going to help repair the results of the other study.

Combined, they're going to say much more about the state of the field!


Edited to Add: 

Looks like Dr. Krishnamurthi's effort sprung from two discussions on social media, which are worth trawling for insight: 

Source: http://cs.brown.edu/~sk/Memos/Examining-Re...