Do better.

There is a twitter hashtag conversation going on called #YesAllWomen, talking about the various dangers that are uniquely faced by women -- a counterpoint to the Not All Men defence.

I got into an argument on twitter about the efficacy of these kind of things. A charitable paraphrase of the tweet that I took umbrage with is essentially: "If I don't do bad things, and I don't know people who are doing bad things, I can't effect change"

Given that I got into an argument, you can understand I think this to be a terrible notion. There's huge numbers of ways you can effect change, and generally help causes even if you aren't part of the problem, or don't know people who are part of the problem.

A common complaint made when issues come up on twitter is that "Twitter won't change anything". While I think that history has started to show us that this isn't entirely true, a thought occurs to me:

If you agree with my thesis on an issue, criticize the crap out my efforts on said issue... but if you really want to change my mind, show me up. Do better than I am. Be more involved than I am, do more than I am, donate more than I am, fundraise more than I am.

I would much rather have you acknowledge that something can be done, and show me how to do it better, than complain that nothing can be done, and that my efforts are wasted and pointless.

What advice do you have for people who are new to this kind of discourse around the tech industry?

I offer this advice to all of the dudebros disturbed by Model View Media and other people and organizations like it: Follow the advice of your Successories poster and do something that scares you every day. Listen to someone who isn't a white man and, this is the tricky part, don't respond. Don't offer your judgment, don't correct, elaborate, or dismiss. Just listen. It's scary, but like anything, it gets easier with repetition. Go on Twitter and follow some new people. Just listen every day for a while -- a few weeks or months -- and resist the urge to explain and correct. Your life, and the lives of those around you, will be immeasurably enriched.

-- Moran Sandquist on Model View Culture

Source: http://modelviewculture.com/pieces/meet-ou...

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.