THE EASIEST

Pick-to-Light Integration

Pick to Light example device

Because it uses the ultimate standard – a simple web URL

https://www.voodoodevices.com/api/DF4660:AFA0CB/message/line1~line2/line3~line4~line5/300,a5,1,g5s,1,f5s,1,d5,1/20r/

  1. Secure Protocol and Server. Use your server prefix
  2. Device ID. Use the specific device ID for your Cloud Display Device
  3. Message Type. Indicate your message type: ‘flash’ or ‘display’
  4. Message Text. Use up to 5 lines for Modern Cloud Display Devices or 2 lines of text for Classic Devices
  5. Custom Tune. Use a custom tune or indicate ‘none’
  6. Seconds and color. Indicate the number of seconds for light illumination and the color

Pick-to-Light QueryString URL Sections

Our Pick-to-Light technology works with your existing ERP or WMS or even Microsoft Excel.  A simple URL request, sent to our server on the Amazon Cloud, causes the associated Pick-to-Light device to light up with the operator’s name, SKU and quantity to pick, barcode, QR-Code, icon – or any five lines of text.

For more powerful interfaces consider using our REST API instead of the QueryString API.

  • Device ID

    Each Pick-to-Light Cloud Display Device has a unique Device ID.

  • Operation Type

    The type of API call. For example: Pick, Flash or Static.

  • Text

    Up to five lines are supported.  You can also encode a barcode, QR-Code or icon (or all three!).  Note that the Cloud Display Devices us two text fields to preserve backward compatibility with our Classic devices.

  • Sound

    Each picker can have a custom tune. First number is the tempo, followed by musical notes. ‘f5s,2’ is an F-sharp in the fifth octave held for two beats.

  • Seconds and Color

    The number of seconds the device will light up, followed by a color code:  ‘r’ for red, ‘g’ for green, ‘b’ for blue.  You can also combine any two of these colors for a total of six options.

Code Samples

These code samples will help you integrate the Pick-to-Light devices with your WMS or ERP.  Need a closed-loop system (to confirm picks)?

#
use strict;
use warnings;
use LWP 5.64;

my $browser = LWP::UserAgent->new;
$browser->cookie_jar({});

my $host = 'https://voodoodevices.com'; # the host
$browser->post( $host,
  [
    'name' => 'yourusername', # the log-in ID
    'pass' => 'yourpassword', # the password for the above ID
    'form_id' => 'user_login_block'
  ],
);

$browser->get($host."api/E8D787:72D9D7/pick/hello/there/15,c5,4/10");
$browser->get($host."api/FFFE81:A42B35/pick/hello/there/15,c5,4/10");
$browser->get($host."api/FAAD4B:8E336A/pick/hello/there/15,c5,4/10");
$browser->get($host."api/FF754A:E24C16/pick/hello/there/15,c5,4/10");
$browser->get($host."api/FB9915:C956FC/pick/hello/there/15,c5,4/10");

File Demo.java


public class Demo {

    public static void main(String[] args)
    {
        try {
            VoodooAPI v = new VoodooAPI();  //create a VoodooAPI object

            v.setUsernameAndPassword("yourusername","yourpassword");
            v.setDeviceID("FAAD4B:8E336A");
            v.setDisplay("hello","there");
            v.setTune("140,c5,1,c5,1,f5,3,p,2,c5,1,f5,1,a5,3");

            v.execute();  //first call will force a login

            v.setDeviceID("FF754A:E24C16");
            v.setDisplay("here's","another");

            v.execute();  //uses the same tune as above

            v.setDeviceID("FB9915:C956FC");
            v.setDisplay("and","another");

            v.execute();  //change only those parameters that you need to

        }
        catch (IllegalArgumentException e) {
            System.out.println ("Illegal Argument Exception, Result = " + e);
        }
        catch (RuntimeException e) {
            System.out.println ("Runtime Exception, Result = " + e);
        }
        catch (Exception e) {
            System.out.println ("Exception, Result = " + e);
        }

    }
}


File VoodooAPI.java


import java.util.*;
import java.net.*;
import java.io.*;

public class VoodooAPI {

    enum operationType {
        display, 
        flash,
        opStatic,
        opStatic2,
        location
    };

    private final String base = "https://www.voodoodevices.com/";
    private String username;
    private String password;
    private String deviceID;
    private String line1;
    private String line2;
    private operationType op = operationType.display;
    private String tune;
    private short time = 10;
    private String txnID;
    private boolean loggedIn = false;
    private List cookies;

    public void VoodooAPI()
    {
    }

    public void setUsernameAndPassword(final String u, final String p)
    {
        username = u;
        password = p;
    }

    public void login() throws Exception
    {
        //make sure there is a username and password set
        if (username=="") {
            throw(new IllegalArgumentException("Username not set"));
        }
        if (password=="") {
            throw(new IllegalArgumentException("Password not set"));
        }

        URLConnection connection = new URL(base).openConnection();
        HttpURLConnection httpConn = (HttpURLConnection)connection;
        httpConn.setInstanceFollowRedirects(false);
        connection.setDoOutput(true); // Triggers POST.
        connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

        String data="name="+username+"&pass="+password+"&form_id=user_login_block";
        try (OutputStream output = connection.getOutputStream()) {
            output.write(data.getBytes());
        }

        InputStream response = connection.getInputStream();
        cookies = connection.getHeaderFields().get("Set-Cookie");

        loggedIn = true;
    }

    public void execute() throws Exception
    {
        //make sure a deviceID has been set
        if (deviceID=="") {
            throw(new IllegalArgumentException("DeviceID not set"));
        }

        //set up the operation
        String opWord;
        switch (op) {
            case display: opWord = "display";
                break;
            case flash: opWord = "flash";
                break;
            case opStatic: opWord = "static";
                break;
            case opStatic2: opWord = "static2";
                break;
            case location: opWord = "location";
                break;
            default: throw(new IllegalArgumentException("Bad operation"));
        }

        //if there is no tune set, set it to none, which implies silence--not even a beep!
        if (tune=="") {
            tune = "none";
        }

        //are we already logged in?  No need to log in twice.
        if (!loggedIn) {
            login();
        }

        //if we get here and we're not logged in, there is some weird problem
        //I don't think this could really happen, because if there is an error, the login above will
        //throw an exception, and the code below will not execute.
        if (!loggedIn) {
            throw(new RuntimeException("Login failed!"));
        }

        //okay, set up the whole url for the get request
        //see:  https://voodoorobotics.com/constructing-a-url/
        String url = base+"api/"+deviceID+"/"+opWord+"/"+line1+"/"+line2;
        switch (op) {
            case display: 
            case flash: 
                url += "/"+tune+"/"+ Integer.toString(time);
                break;
            case opStatic:
            case opStatic2:
            case location:
        ; //do nothing!
        }

        //if there is a transaction ID for the closed loop, then add it here
        //see: https://voodoorobotics.com/closed-loop-system/
        if (txnID!="") {
            url += "/"+txnID;
        }

        try {
            URLConnection connection = new URL(url).openConnection();
            for (String cookie : cookies) {
                connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
            }
            InputStream response = connection.getInputStream();  //response discarded in this demo!
        }
        catch(Exception e) {
            loggedIn = false;  //if there was a problem, make sure that I'm logged out.
            throw e;
        }

    }

    public void setDeviceID(final String id)
    {
        deviceID = id;
    }

    public void setOperation(final operationType ot)
    {
        op = ot;
    }

    public void setDisplay(final String l1, final String l2)
    {
        line1 = l1;
        line2 = l2;
    }

    public void setTune(final String t)
    {
        tune = t;
    }

    public void setTime(final short t)
    {
        time = t;
    }

    public void setTransactionID(final String txn)
    {
        txnID = txn;
    }

}
File -- main.cpp:

#include "voodooapi.h"
#include "iostream"
#include "stdexcept"

using namespace std;

int main(int argc, char *argv[])
{
    (void)argc;     //suppress unused variable warning message!
    (void)argv;     //suppress unused variable warning message!

    try {
        VoodooAPI v;  //create a VoodooAPI object on the stack

        v.setUsernameAndPassword("yourname","yourpassword");
        v.setDeviceID("FAAD4B:8E336A");
        v.setDisplay("hello","there");
        v.setTune("140,c5,1,c5,1,f5,3,p,2,c5,1,f5,1,a5,3");

        v.execute();  //first call will force a login

        v.setDeviceID("FF754A:E24C16");
        v.setDisplay("here's","another");

        v.execute();  //uses the same tune as above

        v.setDeviceID("FB9915:C956FC");
        v.setDisplay("and","another");

        v.execute();  //change only those parameters that you need to

    }
    catch (invalid_argument e) {
        cout << e.what() << '\n';
    }
    catch (runtime_error e) {
        cout << e.what() << '\n';
    }

    return 0;
}
--------------------------------------------------------------------------------------------------------------------------------
File -- voodooapi.h:

#ifndef VOODOOAPI_H
#define VOODOOAPI_H

#include "string"
#include "stdexcept"
#include "curl/curl.h"
#include "iostream"


using namespace std;

class VoodooAPI
{
public:
    enum operationType
    {
        display,
        flash,
        opStatic,
        opStatic2,
        location
    };

    VoodooAPI();
    void setUsernameAndPassword(const string &u,const string &p);
    void login();  //login method could be made private--your choice!
    void execute();
    void setDeviceID(const string &id);
    void setOperation(const operationType ot);
    void setDisplay(const string &l1, const string &l2);
    void setTune(const string &t);
    void setTime(short t);
    void setTransactionID(const string &txn);

private:

    const string base = "https://www.voodoodevices.com/";
    string username;
    string password;
    string deviceID;
    string line1;
    string line2;
    operationType op = display;
    string tune;
    short time = 10;
    string txnID;
    CURL * curly = NULL;
};

#endif // VOODOOAPI_H
--------------------------------------------------------------------------------------------------------------------------------
File voodooapi.cpp:

#include "voodooapi.h"

VoodooAPI::VoodooAPI()
{
    //nothing needs to be done here in this example
}

void VoodooAPI::setUsernameAndPassword(const string &u, const string &p)
{
    username = u;
    password = p;
}

void VoodooAPI::login()
{
    //make sure there is a username and password set
    if (username=="") {
        throw(invalid_argument("Username not set"));
    }
    if (password=="") {
        throw(invalid_argument("Password not set"));
    }

    CURLcode res;

    res = curl_global_init(CURL_GLOBAL_ALL);
    if (res!=CURLE_OK) {
        throw(runtime_error(string("CURL curl_global_init failed: ")+curl_easy_strerror(res)));
    }

    curly = curl_easy_init();  //note that curl_global_init is called automatically in curl_easy_init if not called already
    if (!curly) {
        throw(runtime_error("CURL could not initialize"));
    }

    res = curl_easy_setopt(curly, CURLOPT_POST, 1);  //use a post call to login
    if (res!=CURLE_OK) {
        throw(runtime_error(string("CURL failed CURLOPT_POST: ")+curl_easy_strerror(res)));
    }

    string data="name="+username+"&pass="+password+"&form_id=user_login_block";
    //note: user_login_block is found in the HTML of the homepage--check it out!

    res = curl_easy_setopt(curly, CURLOPT_POSTFIELDS, data.c_str());  //make sure to pass the char *, not the string!
    if (res!=CURLE_OK) {
        throw(runtime_error(string("CURL failed CURLOPT_POSTFIELDS: ")+curl_easy_strerror(res)));
    }
    res = curl_easy_setopt(curly, CURLOPT_COOKIEFILE, ""); //enables the cookie engine without initial cookies
    if (res!=CURLE_OK) {
        throw(runtime_error(string("CURL failed CURLOPT_COOKIEFILE: ")+curl_easy_strerror(res)));
    }

    res = curl_easy_setopt(curly, CURLOPT_URL, base.c_str());  //goes to https://www.voodoodevices.com/
    if (res!=CURLE_OK) {
        throw(runtime_error(string("CURL failed CURLOPT_URL: ")+curl_easy_strerror(res)));
    }

    //now that all the parameters are set, let's go...
    res = curl_easy_perform(curly);
    if (res!=CURLE_OK) {
        throw(runtime_error(string("CURL post request failed: ")+curl_easy_strerror(res)));
    }
}

void VoodooAPI::execute()
{
    //make sure a deviceID has been set
    if (deviceID=="") {
        throw(invalid_argument("DeviceID not set"));
    }

    //set up the operation
    string opWord;
    switch (op) {
    case display: opWord = "display";
        break;
    case flash: opWord = "flash";
        break;
    case opStatic: opWord = "static";
        break;
    case opStatic2: opWord = "static2";
        break;
    case location: opWord = "location";
        break;
    default: throw(invalid_argument("Bad operation"));;
    }

    //if there is no tune set, set it to none, which implies silence--not even a beep!
    if (tune=="") {
        tune = "none";
    }

    //are we already logged in?  No need to log in twice.
    if (!curly) {
        login();
    }

    //if we get here and we're not logged in, there is some weird problem
    //I don't think this could really happen, because if there is an error, the login above will
    //throw an exception, and the code below will not execute.
    if (!curly) {
        throw(runtime_error("Login failed!"));
    }

    //okay, set up the whole url for the get request
    //see:  https://voodoorobotics.com/constructing-a-url/
    string url = base+"api/"+deviceID+"/"+opWord+"/"+line1+"/"+line2;
    switch (op) {
    case display: 
    case flash: 
        url += "/"+tune+"/"+to_string(time);
        break;
    case opStatic:
    case opStatic2:
    case location:
        ; //do nothing!
    }

    //if there is a transaction ID for the closed loop, then add it here
    //see: https://voodoorobotics.com/closed-loop-system/
    if (txnID!="") {
        url += "/"+txnID;
    }

    CURLcode res;

    res = curl_easy_setopt(curly, CURLOPT_HTTPGET, 1L);  //make sure we're doing a GET, not a POST
    if (res!=CURLE_OK) {
        curly = NULL;
        throw(runtime_error(string("CURL failed CURLOPT_HTTPGET: ")+curl_easy_strerror(res)));
    }

    res = curl_easy_setopt(curly, CURLOPT_URL, url.c_str());  //don't pass just url.  curl_easy_setopt expects a (char *)
    if (res!=CURLE_OK) {
        curly = NULL;
        throw(runtime_error(string("CURL failed CURLOPT_URL: ")+curl_easy_strerror(res)));
    }

    res = curl_easy_perform(curly);  //Finally, this is where the real action takes place.
    if (res!=CURLE_OK) {
        curly = NULL;
        throw(runtime_error(string("CURL failed get request: ")+curl_easy_strerror(res)));
    }
}

void VoodooAPI::setDeviceID(const string &id)
{
    deviceID = id;
}

void VoodooAPI::setOperation(const operationType ot)
{
    op = ot;
}

void VoodooAPI::setDisplay(const string &l1, const string &l2)
{
    line1 = l1;
    line2 = l2;
}

void VoodooAPI::setTune(const string &t)
{
    tune = t;
}

void VoodooAPI::setTime(short t)
{
    time = t;
}

void VoodooAPI::setTransactionID(const string &txn)
{
    txnID = txn;
}

function init() 
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL,"https://www.voodoodevices.com/api");
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_USERAGENT, 'PHP script');
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_COOKIEJAR, "cookie.txt");
    curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookie.txt');
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, "name=yourname&pass=yourpassword&form_id=user_login_block");

    ob_start();      // prevent any output
    curl_exec ($ch); // execute the curl command
    ob_end_clean();  // stop preventing output
    curl_close ($ch);
    unset($ch);

}

function lightDevice($devID,$line1,$line2,$time = 10,$flash = TRUE) {

    if ($flash) {
        $cmd = 'message';
    }
    else {
        $cmd = 'pick';
    }

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HEADER, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
    curl_setopt($ch, CURLOPT_COOKIEFILE, "cookie.txt");
    curl_setopt($ch, CURLOPT_URL,"https://www.voodoodevices.com/api/$devID/$cmd/$line1/$line2/15,c5,4/$time");

    $buf3 = curl_exec ($ch);

    curl_close ($ch);
}

init();
lightDevice('E8D787:72D9D7','Fred','Pick 5');
lightDevice('FFFE81:A42B35','Sarah','Ship Today');
lightDevice('FAAD4B:8E336A','John','Package Orders 5 and 6');
lightDevice('FF754A:E24C16','Aaron','Return for Repair');
lightDevice('FB9915:C956FC','Karen','Add to Order 26647');
import requests
session = requests.Session()

def init():

    postData = {
        'name': 'yourusername',
        'pass': 'yourpassword',
        'form_id': 'user_login_block',
        'op': 'Log in'
    }

    loginUrl = 'https://www.voodoodevices.com/api'
    response = session.post(loginUrl, data=postData)

def lightDevice(devID,line1,line2):
    newUrl = 'https://www.voodoodevices.com/api/'+devID+'/message/'+line1+'/'+line2+'/15,c5,4/10'
    response = session.get(newUrl)

init()
lightDevice('E8D787:72D9D7','Fred','Pick 5'); 
lightDevice('FFFE81:A42B35','Sarah','Ship Today'); 
lightDevice('FAAD4B:8E336A','John','Package Orders 5 and 6'); 
lightDevice('FF754A:E24C16','Aaron','Return for Repair'); 
lightDevice('FB9915:C956FC','Karen','Add to Order 26647');

var baseUrl = 'https://www.voodoodevices.com/api';
var sessid = '';
var sessname = '';
function init()
{
    xhr.open('POST', baseUrl+'/user/login/', false);
    xhr.onload = function () {
        // maybe do something on completion
        console.log(this.responseText); //if you're already logged in you'll get a benign error here!
    };
    xhr.onerror = function() {
        console.log(`Error during onload: ${xhr.status}`);
    }
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.withCredentials = true;
    xhr.send('username=yourusername&password=yourpassword');
}

function lightDevice(devID,line1,line2,time = 10,flash = true)
{

    xhr.open('GET', baseUrl+'/'+devID+'/flash/'+line1+'/'+line2+'/'+time+'/');
    //xhr.setRequestHeader('Cookie',sessname+'='+sessid)
    xhr.onload = function () {
        // maybe do something on completion
        console.log(this.responseText);
    };
    xhr.onerror = function() {
        console.log(`Error during onload: ${xhr.status}`);
    }
    xhr.send();
}
init();
lightDevice('E3D985:9EF06F','Thomas','Put it in device 5');
function init()
{
 var formData = {
 'name': 'yourname',
 'pass': 'yourpassword',
 'form_id': 'user_login_block',
 };

 var options = {
 'method' : 'post',
 'payload' : formData,
 'contentType': 'application/x-www-form-urlencoded',
 'muteHttpExceptions': false,
 'followRedirects':false
 };
 var response = UrlFetchApp.fetch('https://www.voodoodevices.com/api', options);

 return response.getAllHeaders()['Set-Cookie'];
}



function lightDevice(devID,line1,line2,time,flash,cookie) 
{
 var cmd = '';
 if (flash) {
 cmd = 'message';
 }
 else {
 cmd = 'pick';
 }
 var options = {
 'muteHttpExceptions': false,
 'headers': {'Cookie':cookie},
 };
 UrlFetchApp.fetch('https://www.voodoodevices.com/api/'+devID+'/'+cmd+'/'+line1+'/'+line2+'/15,c5,4/'+time,options);
}

function go() 
{
 var cookie = init();
 lightDevice('D8927E:C752D3','Karen','Pick 10',10,false,cookie);
 lightDevice('FFFE81:A42B35','John','Put 5',10,false,cookie);
 lightDevice('FAAD4B:8E336A','Scott','Ship with order 6',10,false,cookie);
 lightDevice('FF754A:E24C16','Eric','Move all to Overstock',10,false,cookie);
 lightDevice('FB9915:C956FC','Sarah','Repackage',10,false,cookie);
}
' For Excel be sure to enable 'Microsoft WinHTTP Services' under Tools->References
Global oRequest As WinHttp.WinHttpRequest

Sub Mainsheet_UpdateURL()
    If ([typ] = "Plain") Then
        tp = "pick"
    Else
        If ([typ] = "Flash") Then
            tp = "message"
        Else
            tp = "static"
        End If
    End If
    

    Dim tid As String
    tid = Application.WorksheetFunction.VLookup([LocationName], Worksheets("TagIDs").Range("A2:B9999"), 2, False)
    
    Dim tun As String
    tun = Application.WorksheetFunction.VLookup([Tune], Worksheets("Tunes").Range("A2:B9999"), 2, False)
    [Constructed_URL] = "https://www.voodoodevices.com/api/" & tid & "/" & tp & "/" & [line1] & "/" & [line2]
    
    If Not ([typ] = "Static") Then
        [Constructed_URL] = [Constructed_URL] & "/" & tun & "/" & CStr([theSeconds])
    End If
        
End Sub
Sub Mainsheet_Change(ByVal Target As Range)
    Dim keycells As Range
    
    Set keycells = Range("c6:c16")
    If Not (Application.Intersect(keycells, Range(Target.Address)) Is Nothing) Then

        Call Mainsheet_UpdateURL
       
    End If
    
    'Changes in UserName or Password resets the connection
    Set keycells = Range("c2:c4")
    If Not Application.Intersect(keycells, Range(Target.Address)) Is Nothing Then

        Set oRequest = Nothing
               
    End If

End Sub
Sub Button2_Click()

    'Static oRequest As WinHttp.WinHttpRequest
      
    'On Error GoTo Err_DoSomeJob
    
    If ([theUser] = "") Then
        MsgBox "Username is required!"
        Exit Sub
    End If
    If ([thePassword] = "") Then
        MsgBox "Password is required!"
        Exit Sub
    End If
    If ([theSeconds] = "") Then
        MsgBox "Please set the number of seconds!"
        Exit Sub
    End If
        
    If (oRequest Is Nothing) Then
        
        loginURL = "https://www.voodoorobotics.com/user/login"
        nm = [theUser]
        ps = [thePassword]
        loginBody = "name=" & nm & "&pass=" & ps & "&form_id=user_login&op=Log+in"
        sz = Len(loginBody)
    
        Set oRequest = New WinHttp.WinHttpRequest
        With oRequest
            .Open "POST", loginURL, False
            .SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
            .SetRequestHeader "Content-Length", CStr(sz)
            .Send loginBody
            '.WaitForResponse
            'sResult = .ResponseText
        End With
    End If
    
    
    sUrl = [Constructed_URL]
    With oRequest
        
        .Open "GET", sUrl, False
        .SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
        .Send ""
        '.WaitForResponse
        'sResult = .ResponseText
    
    End With
     
End Sub
' This code does not use WinHTTP.  Instead, it uses a free library called VBA-Web.'
' See https://github.com/VBA-tools/VBA-Web'
' Calls from this code should get through most company firewall setups.'

Sub SKUCall(ByVal sUrl As String)

    If (Client Is Nothing) Then

        Set Client = New WebClient
        
        Client.BaseUrl = "https://www.voodoodevices.com/"
        Client.EnableAutoProxy = True
        Client.FollowRedirects = False
              
        Dim Request As New WebRequest
        Request.Resource = "user/login"
        Request.Method = WebMethod.HttpPost
        Request.AddHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
        Request.Format = WebFormat.FormUrlEncoded
        Request.RequestFormat = WebFormat.FormUrlEncoded
        
        If ([Username] = "") Or ([Password] = "") Then
            MsgBox ("Username or Password not set!")
            Set Client = Nothing
            End
        End If
        
        Request.AddBodyParameter "name", [Username]
        Request.AddBodyParameter "pass", [Password]

        Request.AddBodyParameter "form_id", "user_login"
        Request.AddBodyParameter "op", "Log+in"
        
          ' Make the request'
        Dim Response As WebResponse
        Set Response = Client.Execute(Request)
        
        ' Handle the response'
        If  (Response.StatusCode = 200) Then
            MsgBox ("Login failed!  Sorry.  Too many bad login attempts--maybe?  Wait a while and try again.")
            Set Client = Nothing
            End
        End If
        If Not (Response.StatusCode = 302) Then
            MsgBox ("Login failed!  Sorry.  Error: " & CStr(Response.StatusCode))
            Set Client = Nothing
            End
        End If
        Set Cookies = Response.Cookies
        
    End If
    
    Dim GetRequest As New WebRequest
    GetRequest.Resource = Replace(sUrl, "https://www.voodoodevices.com/", "")
    GetRequest.Method = WebMethod.HttpGet
    Set GetRequest.Cookies = Cookies
    GetRequest.AddHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)"
        
        ' Make the request'
    Dim GetResponse As WebResponse
    Set GetResponse = Client.Execute(GetRequest)
        
        ' Handle the response'
    If Not (GetResponse.StatusCode = WebStatusCode.Ok) Then
        MsgBox ("API call failed!  Sorry.  Error: " & CStr(GetResponse.StatusCode))
        Set Client = Nothing
        End
    End If

End Sub
using System.Text;
using System.Text.Json;

string baseurl = "https://www.voodoodevices.com/api/";

string loginurl = baseurl+"user/login/";
string username = "theuser";
string password = "thepassword";

string device = "DB33A9:13897D";
string deviceEndpoint = baseurl+"device/"+device+"/";

using var client = new HttpClient();

var data = new Dictionary<string, string>
{
    {"username", username},
    {"password", password}
};

var res = await client.PostAsync(loginurl, new FormUrlEncodedContent(data));

var content = await res.Content.ReadAsStringAsync();
Console.WriteLine(content);

JsonDocument doc = JsonDocument.Parse(content);

// Extract the token
JsonElement root = doc.RootElement;
string token = root.GetProperty("token").GetString();

Console.WriteLine("Extracted token: " + token);

client.DefaultRequestHeaders.Add("x-csrf-token",token);
client.DefaultRequestHeaders.Add("referer",baseurl);
data = new Dictionary<string, string>
{
    {"command", "flash"},
    {"line1", "hello"},
    {"color", "green"},
    {"barcode","2345"},
    {"arrow","top"},
    {"quantity","10"}
};

res = await client.PostAsync(deviceEndpoint, new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json"));

content = await res.Content.ReadAsStringAsync();
Console.WriteLine(content);
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;  // for class Encoding
using System.IO;    // for StreamReader
System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12;

namespace CDDDemo
{
    public class IllegalArgumentException: Exception {
        public IllegalArgumentException(string message): base(message) {
        }
    }
    public class RuntimeException: Exception {
        public RuntimeException(string message): base(message) {
        }
    }
    class Program
    {
        static void Main(string[] args)
        {

            try {
                VoodooAPI v = new VoodooAPI();  //create a VoodooAPI object

                v.setUsernameAndPassword("yourusername","yourpassword");
                v.setDeviceID("FAAD4B:8E336A");
                v.setDisplay("hello","there");
                v.setTune("140,c5,1,c5,1,f5,3,p,2,c5,1,f5,1,a5,3");

                v.execute();  //first call will force a login

                v.setDeviceID("FF754A:E24C16");
                v.setDisplay("here's","another");

                v.execute();  //uses the same tune as above

                v.setDeviceID("FB9915:C956FC");
                v.setDisplay("and","another");

                v.execute();  //change only those parameters that you need to

            }
            catch (IllegalArgumentException e) {
                Console.WriteLine("Illegal Argument Exception, Result = " + e);
            }
            catch (RuntimeException e) {
                Console.WriteLine("Runtime Exception, Result = " + e);
            }
            catch (Exception e) {
                Console.WriteLine("Exception, Result = " + e);
            }

        }
        
    }

    public class VoodooAPI
    {

        public enum operationType {
            display, 
            flash,
            opStatic,
            opStatic2,
            location
        };

        private string baseurl = "https://www.voodoodevices.com/";
        private string username;
        private string password;
        private string deviceID;
        private string line1;
        private string line2;
        private operationType op = operationType.display;
        private string tune;
        private short time = 10;
        private string txnID;
        private bool loggedIn = false;
        CookieContainer cookieContainer;

        public VoodooAPI()
        {
            cookieContainer = new CookieContainer();
        }

        public void setUsernameAndPassword(string u, string p)
        {
            username = u;
            password = p;
        }

        public void login()
        {
            //make sure there is a username and password set
            if (username=="") {
                throw(new IllegalArgumentException("Username not set"));
            }
            if (password=="") {
                throw(new IllegalArgumentException("Password not set"));
            }

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(baseurl);
            request.CookieContainer = cookieContainer;
            
            string postData="name="+username+"&pass="+password+"&form_id=user_login_block";
            var data = Encoding.ASCII.GetBytes(postData);

            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;

            using (var stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            var response = (HttpWebResponse)request.GetResponse();

            var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            response.close();

            loggedIn = true;
        }

        public void execute()
        {
            //make sure a deviceID has been set
            if (deviceID=="") {
                throw(new IllegalArgumentException("DeviceID not set"));
            }

            //set up the operation
            string opWord;
            switch (op) {
                case operationType.display: opWord = "display";
                    break;
                case operationType.flash: opWord = "flash";
                    break;
                case operationType.opStatic: opWord = "static";
                    break;
                case operationType.opStatic2: opWord = "static2";
                    break;
                case operationType.location: opWord = "location";
                    break;
                default: throw(new IllegalArgumentException("Bad operation"));
            }

            //if there is no tune set, set it to none, which implies silence--not even a beep!
            if (tune=="") {
                tune = "none";
            }

            //are we already logged in?  No need to log in twice.
            if (!loggedIn) {
                login();
            }

            //if we get here and we're not logged in, there is some weird problem
            //I don't think this could really happen, because if there is an error, the login above will
            //throw an exception, and the code below will not execute.
            if (!loggedIn) {
                throw(new RuntimeException("Login failed!"));
            }

            //okay, set up the whole url for the get request
            //see:  https://voodoorobotics.com/constructing-a-url/
            string url = baseurl+"api/"+deviceID+"/"+opWord+"/"+line1+"/"+line2;
            switch (op) {
                case operationType.display: 
                    url += "/"+tune+"/"+ time.ToString();
                    break;
                case operationType.flash: 
                    url += "/"+tune+"/"+ time.ToString();
                    break;
                case operationType.opStatic:
                case operationType.opStatic2:
                case operationType.location:
                default:
                    break;
                     //do nothing!
            }

            //if there is a transaction ID for the closed loop, then add it here
            //see: https://voodoorobotics.com/closed-loop-system/
            if (txnID!="") {
                url += "/"+txnID;
            }

            try {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.CookieContainer = cookieContainer;

                var response = (HttpWebResponse)request.GetResponse();

                var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
                  //response discarded in this demo!
                response.close();
            }
            catch(Exception e) {
                loggedIn = false;  //if there was a problem, make sure that I'm logged out.
                throw e;  //rethrow the error
            }

        }

        public void setDeviceID(string id)
        {
            deviceID = id;
        }

        public void setOperation(operationType ot)
        {
            op = ot;
        }

        public void setDisplay(string l1, string l2)
        {
            line1 = l1;
            line2 = l2;
        }

        public void setTune(string t)
        {
            tune = t;
        }

        public void setTime(short t)
        {
            time = t;
        }

        public void setTransactionID(string txn)
        {
            txnID = txn;
        }

    }
}

Please refer to our REST API description.

Unboxing and & Setup

YouTube player

Unboxing Your Order

Voodoo Robotics CEO, Trevor Blumenau, goes through each item that may be included in your order.

YouTube player

Pick-to-Light Devices

Everything you ever wanted to know about batteries, (low) voltages, devices, mounting, device IDs, etc.

Check for the latest in AAA battery prices at Amazon.

Double-sided tape can be purchased from Home Depot.

Talk to us about other mounting options.

YouTube player

Turbos

Turbos connect the pick-to-light devices to the cloud.  They are very easy to set up: just set your connection information and add your username and password. Turbos have a very useful GUI that does a great job helping you debug your system, check radio ranges, test communications, and see device parameters.

Using Pick-to-Light with Spreadsheets

Our Pick-to-light system is so flexible and easy to use that you don't have to integrate with a WMS/ERP - if you're a small company you can integrate with your Microsoft Excel Spreadsheets or Google Sheets.  (If you're running Google Sheets, you're probably already aware that it can, at times, be much slower than Excel.)

Microsoft Excel

Just for demonstration purposes, we've constructed an Excel Spreadsheet that is tightly integrated with our Devices.  Please contact us for a copy.  The VBA source code is included.

We do not anticipate that customers will use this spreadsheet in their actual operations, but rather, copy and paste the VBA code into the spreadsheets they're already using.

YouTube player

You May Also be Interested in:

YouTube player

Using the QueryString or REST API is a snap for setting up locations and static information on devices.  In this video, we show how you can do it from Excel.

How easy is that?

YouTube player

Pickers get tired (or perhaps they are lazy!).  Do they really need to push a button on a device to dismiss a pick request?  The answer is, no, they can use a barcode scanner instead.

Trevor Blumenau, CEO of Voodoo Robotics, demonstrates with our Demo Excel Spreadsheet.

Constructing a Sequence of Messages

Oftentimes, pickers or other warehouse/manufacturing workers need to perform a specific sequence of steps. Perhaps they are assembling a kit, or mixing a recipe/formula.

Performance of operations must be in order and only proceed after completion of the previous operation.

In this video, we demonstrate using Devices to deliver a sequence of operational messages in a particular order.  The code is written in VBA and runs in our Demo Excel Spreadsheet.

YouTube player
© Copyright 2024 Voodoo Robotics. All Rights Reserved. Patents Pending. Voodoo Robotics, Voodoo Devices, Big Block Server, and Turbo names and logos are all trademarks of Voodoo Robotics.