Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Factory Activation Example Code 2.1.1
// ------------------------------------------------------------------------------
// File: factory.activation.dut.agent.nut
// Version: 2.1.2
//
// Copyright 2020 Twilio
//
// SPDX-License-Identifier: MIT
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// ------------------------------------------------------------------------------
// ***************************************
// ***** SETUP *****
// ***************************************
// CONSTANTS
// Replace the following string with your BlinkUp API key (provided to Electric Imp customers only)
// And the name of the impCloud that hosts your account: AWS or AZURE.
const API_KEY = "YOUR_BLINKUP_API_KEY";
// GLOBALS
local timeout = 60;
// Replace 'null' in the following line with a hard-coded plan ID
// if you are using a single plan ID
// NOTE this is NOT recommended
local planID = null;
// ***************************************
// ***** DUT AGENT FUNCTIONS *****
// ***************************************
// Set up a receiver for "get.token" messages from the DUT - this is the DUT
// requesting an enrollment token from its own agent, not from the fixture
device.on("dut.get.token", function(dummy) {
// Set up an async request to the Electric Imp token server
local url = "https://api.electricimp.com/v5/enroll"
local headers = {"Content-Type": "application/json",
"Authorization": "Basic " + http.base64encode(API_KEY + ":")};
// If you are re-using a plan ID, it will be picked up and used here
local body = {"type": "production_token"};
if (planID != null) body.plan_id <- planID;
// Make the request for the token
local tokenRequest = http.post(url, headers, http.jsonencode(body));
// Issue the request and handle the response. The device can do nothing until
// it receives the token, so we can handle this request asynchronously
tokenRequest.sendasync(function(result) {
if (result.statuscode == 201) {
// Got a new token - indicated by the '201'
try {
// Convert the incoming data to a table
local data = http.jsondecode(result.body);
// It the token ('token_id') is present, send to the DUT
if ("token_id" in data) {
// Send the token and plan ID to the DUT
device.send("dut.set.token", data);
// Notify the fixture to begin token polling
notifyFixture(data);
}
} catch (err) {
// JSON decode error - report it
server.error(err);
}
} else {
// We didn't get a token so report the status code
// NOTE Real-world code would inform the device or to make
// another attempt to retrieve a token
server.error("Code: " + result.statuscode);
}
});
});
function notifyFixture(data) {
// Send the token to the FIXTURE AGENT (not the DUT AGENT) so it can
// begin polling the server to see when the token is claimed
local url = imp.configparams.factory_fixture_url;
http.post(url, {}, http.jsonencode(data)).sendasync(function(response) {
if (response.statuscode != 200) {
// There was an issue - resend the data
// Real-world code should act according to the response status code
notifyFixture(data);
}
}.bindenv(this));
}
// ------------------------------------------------------------------------------
// File: factory.activation.dut.device.nut
// Version: 2.1.2
//
// Copyright 2020 Twilio
//
// SPDX-License-Identifier: MIT
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// ------------------------------------------------------------------------------
// ***************************************
// ***** SETUP *****
// ***************************************
/// CONSTANTS
// Replace the following strings with your factory WiFi credentials
// NOTE You might alternatively get these via a request to your own server
const SSID = "YOUR_FACTORY_WIFI_SSID";
const PASSWORD = "YOUR_FACTORY_WIFI_PASSWORD";
// GLOBALS
// Flag used to prevent new BlinkUp triggers while BlinkUp is running
local sendingBlinkUp = false;
local blinkUpData = { "ssid": SSID,
"pwd" : PASSWORD,
"plan_id" : "",
"token_id": "" };
// ***************************************
// ** DEVICE UNDER TEST (DUT) FUNCTIONS **
// ***************************************
function blessDeviceUnderTest() {
// Run any tests you require for you DUT
local testSuccess = myTestFunction();
if (testSuccess) {
// DUT passed tests, so define a response handler
// for when the agent returns the enrollment token
// Do this BEFORE calling agent.send() so that the DUT
// is ready as soon as the data comes back (important if
// the factory network is slow)
agent.on("dut.set.token", function(data) {
blinkUpData.token_id = data.token_id;
blinkUpData.plan_id = data.plan_id;
// Attempt to bless this device
server.bless(testSuccess, function(blessSuccess) {
// Set the WiFi credentials and reboot if blessing completes successfully
if (blessSuccess) {
imp.clearconfiguration();
imp.setenroltokens(blinkUpData.plan_id, blinkUpData.token_id);
if (blinkUpData.ssid.len() != 0) imp.setwificonfiguration(blinkUpData.ssid, blinkUpData.pwd);
// At this point, the DUT goes idle. 60s later
// the Squirrel VM is restarted
}
}.bindenv(this));
}.bindenv(this));
// The message response handler defined, we can now request a token
agent.send("dut.get.token", true);
}
}
// ***************************************
// ***** YOUR HARDWARE TEST CODE *****
// ***************************************
function myTestFunction() {
// Add your tests here
// NOTE Returning false will prevent blessing
return true;
}
// ***************************************
// ***** RUNTIME START *****
// ***************************************
// Wait for idle before proceeding in order to ensure imp.configparams
// has been populated on the device (in case it wakes from deep sleep)
imp.onidle(function() {
// Check that we are running in the factory
if ("factoryfirmware" in imp.configparams) {
// The DUT is running factory firmware, ie. it's in a factory
// so test and bless the DUT
blessDeviceUnderTest();
} else {
server.error("This DUT (ID: " + hardware.getdeviceid() + ") is not operating in the factory.");
}
}.bindenv(this));
// ------------------------------------------------------------------------------
// File: factory.activation.fixture.agent.nut
// Version: 2.1.2
//
// Copyright 2020 Twilio
//
// SPDX-License-Identifier: MIT
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// ------------------------------------------------------------------------------
// ***************************************
// ***** SETUP *****
// ***************************************
// CONSTANTS
// Replace the following string with your BlinkUp API key (provided to Electric Imp customers only)
// And the name of the impCloud that hosts your account: AWS or AZURE.
const API_KEY = "YOUR_BLINKUP_API_KEY";
// GLOBALS
local timeout = 60;
// ***************************************
// ***** FACTORY FIXTURE AGENT CODE ****
// ***************************************
// Define the function that polls the impCloud for a claimed enrollment token
function poll(token, cb = null) {
// Create the request to send to the server
local path = "https://api.electricimp.com/v5/enroll/" + token;
local headers = {"Authorization": "Basic " + http.base64encode(API_KEY + ":")};
local result = http.get(path, headers).sendsync();
if (result.statuscode == 200) {
// Got a positive response from the server - try to decode the returned JSON
try {
local data = http.jsondecode(result.body);
if ("claimed_at" in data && data.claimed_at.len() > 0) {
// The key 'claimed_at' in the data with a non-empty value indicates
// that the token has been claimed, or an API error occurred
if (cb != null) cb(data);
return;
} else {
// Token not (yet) claimed
if (timeout > 0) {
timeout--;
} else {
if (cb != null) cb({"err": "Token claim timeout"});
return;
}
}
} catch (err) {
// Ignore JSON decode error - not much we can do here except poll again
}
} else {
if (cb != null) cb({"err":format("Poll failed with status code %i", result.statuscode)});
}
// Poll again in two seconds’ time
imp.wakeup(2.0, function() {
poll(token, cb);
}.bindenv(this));
}
// This is a FACTORY FIXTURE AGENT routine to receive the token data sent by the
// DUT’s agent and to poll the server for the claimed token
http.onrequest(function(request, response) {
// Attempt to decode the JSON sent by the DUT's agent
try {
local data = http.jsondecode(request.body);
if ("token_id" in data) {
// Start polling for the token claim
timeout = 60;
poll(data.token_id, function(pollData) {
if ("err" in pollData) {
server.error("Polling error: " + pollData.err);
} else {
// Make use of the data received from the poll
useData(pollData);
}
}.bindenv(this));
response.send(200, "OK");
} else {
response.send(400, "Bad data - no enrollment token included");
}
} catch (err) {
// Log the error and inform the DUT agent
server.error(err);
response.send(500, err);
}
});
function useData(data) {
// This function is a placeholder for the customers own. Here we simply
// log the DUT’s ID and agent URL, but real-world code might, for example,
// send this data to a label printer or laser etching station
server.log("Production Device device ID: " + data.device_id);
server.log("Production Device agent URL: " + data.agent_url);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment