api workshop
DESCRIPTION
TRANSCRIPT
APIs for your Hardware
Electric Imp
Matt HainesCommunity Manager@beardedinventor
● HTTP Handler Deep Dive ● APIs that change hardware states● APIs that read hardware states
What are we doing tonight
The Circuit
HTTP Handlers
● The HTTP Handler is the code that executes when your agent receives an incoming http request
● Register your handler with: http.onrequest(HTTPHandler)
● http://electricimp.com/docs/api/httphandler/
Basic Handlerfunction HTTPHandler(req, resp) {
// ALWAYS WRAP YOUR HANDLERS IN A TRY/CATCH
try {
// "200: OK" is standard return message
resp.send(200, "OK");
} catch (ex) {
// Send 500 response if error occured
resp.send(500, format("Agent Error: %s", ex));
}
}
The Request table
method - The method (GET, PUT, POST, etc)path - The HTTP path minus the agentURLquery - Table containing the query parametersbody - The body of the requestheaders - Table containing the request headers
Let’s make an APIfunction HTTPHandler(req, resp) {
try {
// "200: OK" is standard return message
logRequest(req);
resp.send(200, "OK");
} catch (ex) {
// Send 500 response if error occured
resp.send(500, format("Agent Error: %s", ex));
}
}
function logRequest(req) {
server.log(format("Method: %s", req.method));
server.log(format("Path: %s", req.path));
server.log(format("Body: %s", req.body));
server.log("Query Parameters:");
foreach(k,v in req.query) {
server.log(format("%s: %s", k, v));
}
server.log("Headers:");
foreach(k,v in req.headers) {
server.log(format("%s: %s", k, v));
}
}
Let’s make some requests// basic requestcurl "agenturl"
// set the methodcurl -X POST "agenturl"
// set a path and some query parameterscurl -X POST "agenturl/api?led1=1&led2=test"
// set a headercurl -X PUT -H "apikey:123" "agenturl"
// send some data in the bodycurl -X PUT -H "apikey:123" "agenturl" --data "{ \"foo\": \"bar\" }"
● Use this information to build powerful APIs:○ path - what resource we’re interacting with○ method - what we’re doing with the resource○ body/query - data we need○ headers - authentication, etc
● Example:
// Get the state of the LEDcurl -X GET "agenturl/led"
// Set the state of the LEDcurl -X GET -H "apikey: 123" "agenturl/led" --data "{ \"state\": 1 }”
So what..
APIs to Set Hardware
● When we get a request○ Check the verb○ Check the path○ Validate the data○ Send the data to the device○ Send a response
● When the device gets a message○ Do something with it
Simple Device Code// configure hardware
led <- hardware.pin9;
led.configure(DIGITAL_OUT);
// set LED from server
agent.on("setLed", function(state) {
led.write(state);
});
Let’s make an API// here's the important bit
local path = req.path.tolower();
if (req.method == "POST") {
if (path == "/led" || path == "/led/") {
if (req.body != null && req.body.len() > 0) {
local data = http.jsondecode(req.body);
if ("state" in data) {
device.send("setLed", data.state.tointeger());
resp.send(200, "OK");
}
}
resp.send(406, "Not Acceptable - Missing 'state'");
}
}
// wrong urlcurl -X POST "agenturl"
// proper url, but no datacurl -X POST "agenturl/led"
// proper url, and proper datacurl -X POST "agenturl/led" --data "{ \"state\": 0 }"
Let's make some requests
● Two ways to query data from the device:○ Send data from device to agent whenever state
changes (or at regular intervals)○ Make a round trip from agent → device → agent
● First method is easy, and fast, but no guarantee data is up to date
● Second method requires a LOT more code, and adds some time (as we need to make a round trip to the device)
What about reading data
Sending Data to the Device// configure hardware
led <- hardware.pin9;
led.configure(DIGITAL_OUT);
function setLed(state) {
led.write(state);
agent.send("ledState", state);
}
agent.on("setLed", setLed);
button <- hardware.pin1;
function buttonStateChange() {
setLed(1-led.read()); // invert state
}
button.configure(DIGITAL_IN_PULLUP, buttonStateChange);
Catch and Store Message in AgentledState <- "Unknown";
agent.on("ledState", function(state) {
ledState = state;
}
Let's make an API// here's the important bit
if (req.method == "GET") {
if (path == "/led" || path == "/led/") {
resp.send(200, http.jsonencode({state = ledState}));
}
}
Let's make some requests
// wrong urlcurl -X GET "agenturl"
// proper urlcurl -X GET "agenturl/led"
What about security
● All communication is HTTPS● Agent URLs can't be crawled● There are 6412 so guessing them is.. hard ● Security by obscurity● API Keys● Basic Auth● HMAC-SHA Signatures
What's Next
● Write webpages or apps to interact with API
● Try implementing some of the auth methods● Class to handle/delegate incoming requests
● Long polling● Serve HTML pages from HTTP Handler