THE EASIEST

Pick-to-Light REST Integration

Pick to Light example device

Simple!  The way it should be.

Two different APIs

Voodoo Robotics supports two different API’s for interacting with Pick-to-Light devices.

Simple

You can use a simple URL with a GET request, similar to entering a URL into your browser.  This does not use REST principles.

Advanced

Use REST for very powerful integrations.  It’s slightly more complicated, but really worth the trouble.

The examples on this page show you how to really leverage the power of REST with Modern and Classic devices.  You can write your code in any language you choose.  We’ve chosen PHP just for demonstration purposes–it’s fairly easy for any programmer to read PHP and understand the critical steps.

Be sure to read the comments around the code.  They can be very useful, particularly if you don’t know PHP.

  • Get a list of all devices in your account.  Use a GET request on the endpoint https://www.sku-keeper.com/api2/device/.  As with most REST servers, you can use XML or JSON in your request.  See the examples below.

  • Inspect Request

    Once you have a particular DeviceID, you can get more information about that device (voltage, uptime, etc.).  Use a GET request on the endpoint https://www.sku-keeper.com/api2/device/FFFFFF:EEEEEE/.  As above, you can use XML or JSON in your request.

  • Update Request

    You can also update the message on a Device.  Use a POST request on the endpoint https://www.sku-keeper.com/api2/device/FFFFFF:EEEEEE/.  You can submit your data as application/xml or application/json.  Notice that POST and PUT are equivalent in our REST API since there is no object creation mechanism.

  • Update Multiple

    You can also update multiple Devices in one API call.  Use a POST request on the endpoint https://www.sku-keeper.com/api2/devices/.  (Note the plural ‘devices’.)  You can submit your data as application/xml or application/json.  This saves time and resources by consolidating API calls.  See the example below.

  • Inspect Turbos

    In a similar way to the device endpoint, you can use the turbo endpoint to get information about your Turbos.  Use a GET request on the endpoint https://www.sku-keeper.com/api2/turbo/, or https://www.sku-keeper.com/api2/turbo/SERIALNO/.

Step by Step (in PHP)

Let’s walk through it together…

//This code is written in PHP.  (Revised May 2020)

//First define an initialization function to log us in with username and password
//The key thing is that we have to capture both the session cookie and the csrf token
//They will both be stored in the global $ch.
function init()
{
    global $ch;  //PHP is silly in the way scoping rules work!
    $ch = curl_init();  //initialize the cURL object

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/user/login/");
    //note that we're using api2 and we first access the login endpoint

    curl_setopt($ch, CURLOPT_COOKIEJAR, "/tmp/cookie.txt");
    //use a cookiejar, because the session ID will be stored in the cookie

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    //silly cURL, it should be like this by default!  This alows us to do $x = curl_exec()

    curl_setopt($ch, CURLOPT_POST, true);
    //login requires a POST with username and password content

    curl_setopt($ch, CURLOPT_HTTPHEADER,array( //specify XML for input and output (could use JSON instead)
        "content-type: application/xml",  //format of the outbound data/commands
        "accept: application/xml",				//format of the inbound response
    ));
    //we could have used JSON, but let's stick to both input and output in XML for simplicity

    //curl_setopt($ch, CURLOPT_VERBOSE, 1);
    //uncomment the above line if you're having problems -- curl will give you some diagnostics

    $xml = "
        <item>
          <username>yourusername</username>
          <password>yourpassword</password>
        </item>
    ";

    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

    $theXml = curl_exec($ch);	// this logs us in, puts a session cookie in the cookiejar, and
              		// returns some XML that contains our super-secret token

    $response = simplexml_load_string($theXml);  // parse the XML that was returned

    curl_setopt($ch, CURLOPT_HTTPHEADER,array(		//do what we did before, but this time add the csrf token
                "content-type: application/xml",			//format of the outbound data/commands
                "accept: application/xml",						//format of the inbound response
                "x-csrf-token: ".$response->token,
            ));
    //this is very important:  the above updates the header with the token
    //and since $ch is global, it's maintaining the cookie and the csrf token
    //we'll rely on this in the functions below
    //some would argue that a CSRF token is overkill, but cookies are not very well protected
    //these days.  For more information about session riding attacks, see
    //https://en.wikipedia.org/wiki/Cross-site_request_forgery
}

//As an example of getting device information, we get the voltage
//Just pass the function the deviceID like this:
//
//   print(getVoltage('FD02EF:AB9A16'));
//
function getVoltage($devID)
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, false);
    //make sure we're doing a GET request

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/device/".$devID."/");
    //to get info on a device, append the device ID to the endpoint

    $theXml = curl_exec($ch);	// make the request -- the XML returned has everything in it

    $response = simplexml_load_string($theXml);  // parse the XML that was returned

    //print_r($response);  //uncomment this line to see all the fields you can access
    return $response->voltage; //we're only after the voltage
}

//What if we didn't know what our deviceID's were?  Here's a function that will return an array
//of all our device IDs.
function arrayOfDevices()
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, false);
    //make sure we're doing a GET request

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/device/");
    //without a device ID in the URL, we get a list of all of them

    $theXml = curl_exec($ch);	// make the request -- the XML returned has everything in it

    $response = simplexml_load_string($theXml);  // parse the XML that was returned

    return (array)$response->item; //result is in the 'item'
}

//Now we show how we can _set_ device information.  Setting the command and associated fields
//forces a call to the device to light up.  If the command is 'flash', the light flashes.
//If the command is 'display', the light is solid.  (We recommend flash, as it uses less power.)
//For a command, you can set 'line1', 'line2', 'line3', 'line4', 'line5', 'sound', 'seconds', 'color',
//and 'nonce'.  Only our V2/Modern devices support lines3-5 and color.  The nonce is used
//by our feedback protocol.  See https://voodoorobotics.com/closed-loop-system/
//Sounds strings are just the tempo followed by a series of notes.  Check out our page at
//https://voodoorobotics.com/constructing-a-url/ for some examples (Section 7)
//Using long sounds chews up batteries--quickly!  '15,c5,4' is a simple beep.
//Check out new battery estimator at https://voodoorobotics.com/pick-light-batteries/
function messageDevice($devID,$line1,$line2,$time = 10,$sound = '15,c5,4')
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, true);
    //updating the static data or making the device flash requires a post

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/device/".$devID."/");
    //append the deviceID to the endpoint.  (The response is ignored.)

    $xml = "
        <item>
          <API>2.0</API>
          <command>flash</command>
          <line1>$line1</line1>
          <line2>$line2</line2>
          <seconds>$time</seconds>
          <sound>$sound</sound>
        </item>
    ";

    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

    curl_exec($ch); // make the request
}

//The following function sets the static display of the device--what it displays by default
//when there are no active commands (like above).  V2/Modern devices rely on 'statica', 'staticb'
//'staticc', 'staticd', 'statice', while the classics use 'static' and 'static2'
//You can also set the default display timeout (not to be confused with 'seconds' above) with
//'timeout'.  Set a location override with 'location_override' and brightness with, yes, 'brightness'
//(a value between 0 and 100).
function displayOnDevice($devID,$line1,$line2)
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, true);
    //updating the static data or making the device flash requires a post

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/device/".$devID."/");
    //append the deviceID to the endpoint.  (The response is ignored.)

    $xml = "
        <item>
          <API>2.0</API>
          <static>$line1|$line2</static>
          <statica>$line1</statica>
          <staticb>$line2</staticb>
        </item>
    ";
    //Note that we do not clear the other static fields here.  You may or may not want
    //to do that in your code.

    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

    curl_exec ($ch); // make the request
}

//Want something fancier?  Here's some php to put a barcode on a V2 device
//Use '\bc' for barcode, '\ic' for icon, and '\qr' for a QR Code
//For a list of possible icons see this discussion https://voodoorobotics.com/line-encodings
function v2messageWithBarcode($devID,$line1,$line2,$bc,$time = 10)
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, true);
    //updating the static data or making the device flash requires a post

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/device/".$devID."/");
    //append the deviceID to the endpoint.  (The response is ignored.)

    $xml = "
        <item>
          <API>2.0</API>
          <command>flash</command>
          <line1>$line1</line1>
          <line2>$line2</line2>
          <line3>\bc$bc</line3>
          <seconds>$time</seconds>
        </item>
    ";

    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

    curl_exec($ch); // make the request
}


//Hell, let's go over the top!  Here's a function to make the device light up and then set the background
//data at the same time.  Whoo hoo!  What a waste (of batteries, etc.)  Don't do this, puhleeze.
function v2veryFancy($devID,$line1,$line2,$bc,$ic,$qr,$sound,$time,$color,$sline1,$sline2,$sbc,$sic,$sqr)
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, true);
    //updating the static data or making the device flash requires a post

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/device/".$devID."/");
    //append the deviceID to the endpoint.  The response is ignored, but let's stick to XML

    $xml = "
        <item>
          <API>2.0</API>
          <command>flash</command>
          <line1>$line1</line1>
          <line2>$line2</line2>
          <line3>\bc$bc</line3>
          <line4>\ic$ic</line4>
          <line5>\qr$qr</line5>
          <sound>$sound</sound>
          <seconds>$time</seconds>
          <color>$color</color>
          <statica>$sline1</statica>
          <staticb>$sline2</staticb>
          <staticc>\ic$sic</staticc>
          <staticd>\bc$sbc</staticd>
          <statice>\qr$sqr</statice>
        </item>
    ";

    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

    curl_exec($ch); // make the request
}

//What about information about the Turbos?
//To get a list of Turbos in your account try this:
function arrayOfTurbos()
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, false);
    //make sure we're doing a GET request

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/turbo/");
    //without a device ID in the URL, we get a list of all of them

    $theXml = curl_exec($ch);	// make the request -- the XML returned has everything in it

    $response = simplexml_load_string($theXml);  // parse the XML that was returned

    return (array)$response->item; //result is in the 'item'
}

//Or to get specific information about a particular Turbos
function turboInfo($turboID)
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, false);
    //make sure we're doing a GET request

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/turbo/".$turboID."/");
    //without a device ID in the URL, we get a list of all of them

    $theXml = curl_exec($ch);	// make the request -- the XML returned has everything in it

    $response = simplexml_load_string($theXml);  // parse the XML that was returned

    //print_r($response);  //uncomment this line to see all the fields you can access

    return (array)$response->item; //result is in the 'item'
}


//Okay, we saved the most useful for last:
//How about a single API call to light up multiple devices?
//Here we create an Array in XML for several commands to several Devices
//Note that we use the 'devices' endpoint, not the 'device' endpoint
function helloTimesFive($id1,$id2,$id3,$id4,$id5)
{
    global $ch;
    if (empty($ch)) init();  //initialize with the cookie and the csrf token if needed

    curl_setopt($ch, CURLOPT_POST, true);
    //updating the static data or making the device flash requires a post

    curl_setopt($ch, CURLOPT_URL,"https://www.sku-keeper.com/api2/devices/");
    //append the deviceID to the endpoint.  (The response is ignored.)

    //notice how building an array in XML is kind of tricky!  Using JSON to form an array is easier
    //We encourage the use of JSON instead, but we use XML here for consistency
    $xml = "
        <items>
            <array is_array='true'>
                <item>
                    <API>2.0</API>
                 	<deviceID>$id1</deviceID>
                    <command>flash</command>
                    <line1>Hello</line1>
                    <line2>Howdy</line2>
                    <seconds>10</seconds>
                    <sound>15,c5,4</sound>
                </item>
         	<item>
                    <API>2.0</API>
                 	<deviceID>$id2</deviceID>
                    <command>flash</command>
                    <line1>Hello</line1>
                    <line2>Howdy</line2>
                    <seconds>10</seconds>
                    <sound>15,c5,4</sound>
                </item>
                <item>
                    <API>2.0</API>
                 	<deviceID>$id3</deviceID>
                    <command>flash</command>
                    <line1>Hello</line1>
                    <line2>Howdy</line2>
                    <seconds>10</seconds>
                    <sound>15,c5,4</sound>
                </item>
         	<item>
                    <API>2.0</API>
                 	<deviceID>$id4</deviceID>
                    <command>flash</command>
                    <line1>Hello</line1>
                    <line2>Howdy</line2>
                    <seconds>10</seconds>
                    <sound>15,c5,4</sound>
                </item>
                <item>
                    <API>2.0</API>
                 	<deviceID>$id5</deviceID>
                    <command>flash</command>
                    <line1>Hello</line1>
                    <line2>Howdy</line2>
                    <seconds>10</seconds>
                    <sound>15,c5,4</sound>
                </item>
            </array>
        </items>
    ";

    //if we were using JSON, we'd use
    $json = "
        [
           {
              'API': '2.0',
              'deviceID': $id1,
              'command': 'flash',
              'line1': 'Hello',
              'line2': 'Howdy',
              'seconds': '10',
              'sound': '15,c5,4'
           },
           {
              'API': '2.0',
              'deviceID': $id2,
              'command': 'flash',
              'line1': 'Hello',
              'line2': 'Howdy',
              'seconds': '10',
              'sound': '15,c5,4'
           },
           {
              'API': '2.0',
              'deviceID': $id3,
              'command': 'flash',
              'line1': 'Hello',
              'line2': 'Howdy',
              'seconds': '10',
              'sound': '15,c5,4'
           },
           {
              'API': '2.0',
              'deviceID': $id4,
              'command': 'flash',
              'line1': 'Hello',
              'line2': 'Howdy',
              'seconds': '10',
              'sound': '15,c5,4'
           },
           {
              'API': '2.0',
              'deviceID': $id5,
              'command': 'flash',
              'line1': 'Hello',
              'line2': 'Howdy',
              'seconds': '10',
              'sound': '15,c5,4'
           }
        ]
    ";
    //this demo ignores the above $json variable.  If you want to use it, you'd have to
    //modify the headers in init()

    //A special note for Stand-Alone Server users only: (most customers can ignore this)
    //Another reason to use JSON is that the XML above will not work on the Stand-Alone Server!
    //Your XML for the Stand-Alone Server must be formatted as
    // <items><list-item>...</list-item><list-item>...</list-item></items>
    //Yuck!

    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);

    curl_exec($ch); // make the request
}

You May Also be Interested in:

Device Limitations

The REST API works for both Modern and Classic devices.

Modern devices support five lines both for commands and for static data.  As you can see in the code example we use line 1-5 and static a-e for these lines.  The device display will scale to fit the text that you send it, so if you have a five character string on only one line, it’ll be much larger than five 26-character lines.  Each line supports up to 26 characters.

Classic devices support two lines only.  The display is 16 characters wide, but you can send a maximum of 32 characters.  If you send more than 16 characters, the display will scroll back and forth, left to right.  Classic devices require a ‘|’ to separate lines in a static call, and support both ‘static’ and ‘static2’.  Static is shown after a single button push, and static2 is shown after two button pushes.

Barcodes, QR Codes, and Icons

Any command or static lines on a Modern device can be replaced with a barcode, QR Code or icon.  As you can see in the example above, if you preface a line with ‘\bc’ you will get a barcode.  If you preface a line with ‘\qr’ you will get a QR Code.  And if you preface a line with ‘\ic’ you will get an icon.  And, of course, you could do all three!

Since lines on Modern devices are limited to 26 characters, barcodes and QR Codes are limited to 23 characters (three are used for the prefix).  Note that bitly links are 22 characters — so any URL can be encoded on a device.

Modern devices also support two main types of icons, arrows and hazard signs.

Learn about the different encoding formats for icons.

© Copyright 2020 Voodoo Robotics. All Rights Reserved. Patents Pending. Voodoo Robotics, SKU-Keeper, SKU-Turbo, and SKU-Station logos are all trademarks of Voodoo Robotics.