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 APIs for interacting with Pick-to-Light devices.

Integration between your warehouse management system (WMS) or Enterprise Resource Management Solution (ERP) is easy.

  • Simple. The easiest pick-to-light integration is the simple query string with a GET request. We suggest using this for initial evaluation. This does not use REST principles.
  • Advanced. For more powerful interfaces, consider using our REST API. It is slightly more complicated, but really worth the trouble.

This is a playlist of REST API tutorials on our Voodoo Robotics YouTube Channel

  • Get a list of all devices in your account.  Use a GET request on the endpoint https://www.voodoodevices.com/api/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.voodoodevices.com/api/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.voodoodevices.com/api/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.voodoodevices.com/api/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.voodoodevices.com/api/turbo/, or https://www.voodoodevices.com/api/turbo/SERIALNO/.

The examples on this page show you how to really leverage the power of REST with our modern Cloud Display Devices.  You can write your code in any language you choose.  We have chosen Python for demonstration purposes because it is fairly easy for any programmer to read Python and understand the critical steps.

Be sure to read the comments around the code.  They can be very useful, particularly if you do not know Python.  If you prefer PHP, please go to the REST API using PHP page.

Most Simple Example (Python)

In just five lines of Python code, you can send a simple command to a device.

import requests,json
session = requests.Session()
url = "https://www.voodoodevices.com/api/"
x = session.post(url+"user/login/",json={'username': 'yourusername',
    'password':'yourpassword'})
z = json.loads(x.text)
session.post(
    url+"device/E8D008:619874/",
    headers = {'referer': url,'x-csrf-token': z['token'],},
    json={'command':'flash','line1':'Hello','line2':'There'},
)

#That was just FIVE LINES of CODE (plus a variable assignment)

#Here's a function and a function call
def simplePickCommand(deviceID,line1,line2):
    session.post(
        url+"device/"+deviceID+"/",
        headers = {'referer': url,'x-csrf-token': z['token'],},
        json={'command':'flash','line1':line1,'line2':line2},
    )

#now let's call the function
simplePickCommand('E8D008:619874','Ben','Take 5')
simplePickCommand('E476EA:EA3329','Fred Put','Red Shirts Here')

Step by Step (in Python)

Let’s walk through it together…

###################################################################################################
#                                                                                                 #
# Function Overview for Cloud Display Devices in Python                                           #
#                                                                                                 #
# This Python script demonstrates how to use the REST API for Cloud Display Devices,              #
# including the latest features such as displaying barcodes, quantities, QR codes, and arrows     #
# without needing line prefixes.                                                                  #
#                                                                                                 #
###################################################################################################

# We use the Python requests library
import requests

# For pretty printing Python dictionaries and arrays in this demo, we use the json.dumps function
import json

# For simplicity, we use API_KEY authentication here.  To obtain an API_KEY, you need to create an API user
# on Big Block.  Then login as that user and view the user profile.
# Replace 'your_api_key_here' with your actual API key obtained there.
API_KEY = 'your_api_key_here'

# The following base endpoint should be modified to point to your server. In this example, we use the 
# multi-tenant server on AWS.
url = 'https://www.voodoodevices.com/api/'

# Set up the headers to include your API key
headers = {
    'API-KEY': f'{API_KEY}',
    'Content-Type': 'application/json',
}

# Now we define some functions:

def get_device_info(device_id):
    """Get Device Info
    This function retrieves all available Cloud Display Device attributes from Big Block.

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device. 

    Returns:
        A dictionary of attributes for the specific Cloud Display Device.
    """
    response = requests.get(f"{url}device/{device_id}/", headers=headers)
    return response.json()

# This is how you would use the get_device_info function:

# device_id = 'D4E825:8B665D'
# device_info = get_device_info(device_id)
# input("Press Enter to see all attributes that the Cloud Display Device stores")
# print(json.dumps(device_info, indent=4))

# To access some individual attributes:
# print("Last Reported Voltage: ", device_info["lastvoltage"])
# print("Location: ", device_info["locationoverride"])
# or, by using variables:
# attribute_label = "Voltage: "
# attribute = "lastvoltage"
# print(attribute_label, device_info[attribute])

# The code works if you already know the device_id. But what if you don't? Here's how you get a list
# of all your device IDs.

def get_device_list():
    """Get Device List

    A function that returns a list of all Device IDs in Big Block.

    Returns:
        A list of device IDs.
    """
    response = requests.get(f"{url}device/", headers=headers)
    return response.json()

# Here's how you'd use this function:

# device_list = get_device_list()
# input("Press Enter to see a list of all Cloud Display Devices")
# print(json.dumps(device_list, indent=4))

# Here's a function that helps light up a device with a message
def message_device(device_id, command='flash', lines=None, time=10, sound='15,c5,4', color='r', 
                   barcode=None, quantity=None, qrcode=None, arrow=None):
    """Message Device

    This function illustrates how to send a command to a device to force it to light up.

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device.
        command (str): Specifies the light behavior for the device. Defaults to 'flash'.
        lines (list): A list of up to 5 lines of text to display. (Longer than 26 characters is truncated.)
        time (int): The amount of time that the device lights up before timing out. Defaults to 10.
        sound (str): The sound that the device makes when lighting up. Defaults to '15,c5,4'.
        color (str): The color of the LED that lights up for the command. Defaults to 'r'.
        barcode (str): The string to display as a barcode without using the 'bc' prefix.
        quantity (int): The quantity to display in a box without using the 'qt' prefix.
        qrcode (str): The string to display as a QR code without using the 'qr' prefix.
        arrow (str): Direction of the arrow to display without using the 'ic' prefix. Some options include 
        (with redundancies!) 'up', 'top', 'down', 'bottom', 'left', 'right', 'topleft', 'topright', 
              'bottomleft', 'bottomright'.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'command': command,
        'seconds': time,
        'sound': sound,
        'color': color,
    }

    # Add lines to the payload
    if lines:
        for idx, line in enumerate(lines, start=1):
            payload[f'line{idx}'] = line

    # Add new features if provided
    if barcode:
        payload['barcode'] = barcode
    if quantity is not None:
        payload['quantity'] = quantity
    if qrcode:
        payload['qrcode'] = qrcode
    if arrow:
        payload['arrow'] = arrow

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# Examples of using the message_device function:

# 1. Display a message with a barcode:
# message_device(
#     device_id='D4E825:8B665D',
#     lines=['Scan this barcode:'],
#     barcode='abc123',
# )

# 2. Display a message with a quantity:
# message_device(
#     device_id='D4E825:8B665D',
#     lines=['Pick the following quantity:'],
#     quantity=5,
# )

# 3. Display a message with a QR code:
# message_device(
#     device_id='D4E825:8B665D',
#     lines=['Scan this QR code:'],
#     qrcode='cde456',
# )

# 4. Display a message with an arrow:
# message_device(
#     device_id='D4E825:8B665D',
#     lines=['Proceed in this direction:'],
#     arrow='up',
# )

# Our feedback protocol uses nonces. See https://voodoorobotics.com/closed-loop-system/.
# Here's a similar function that sets the nonce.
def message_device_with_nonce(device_id, line1, nonce, time=10, sound='15,c5,4', color='r'):
    """Message Device with Nonce

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device.
        line1 (str): Line 1 of the message.
        nonce (str): A string or unique number that serves as feedback when acknowledging the message.
        time (int): The amount of time that the device lights up before timing out. Defaults to 10.
        sound (str): The sound that the device makes when lighting up. Defaults to '15,c5,4'.
        color (str): The color of the LED that lights up for the command. Defaults to 'r'.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'command': 'flash',
        'line1': line1,
        'nonce': nonce,  # Nonce added for feedback
        'seconds': time,
        'sound': sound,
        'color': color,
    }

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# You might call this function like this:
# message_device_with_nonce(device_id='D4E825:8B665D', line1='Line1', nonce='Nonce123')

# Beyond feedback, setting a nonce allows you to do this:
def kill_message(device_id, nonce):
    """Kill Message
    Terminate a currently pending command.
    To use kill function, you must reference the command both by the device ID and the nonce 
    you originally used when you sent the command.

    Args:
        device_id (str): A unique alphanumeric string that identifies the Cloud Display Device.
        nonce (str): A string or unique number that identifies the specific command to kill.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'command': 'kill',
        'nonce': nonce,
    }

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# You can also remove ALL pending commands from a device with a reset.
def reset_device(device_id):
    """Reset Device
    Resetting a device removes all commands in the device's queue (including overlapped commands).

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'command': 'reset',
    }

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# Another way to remove all pending commands is to completely reboot a device.
def reboot_device(device_id):
    """Reboot Device
    Rebooting a device is like power-cycling the device, i.e. replacing the batteries.
    It causes all pending commands to be lost.

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'command': 'reboot',
    }

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# Between commands, when the device is idle, it displays what we call 'static' text. You might think 
# of this as the 'default' display--what is displayed when there is no command pending. Here's how you 
# would set the static text on a device.
def set_static_message(device_id, statics, timeout=5, location_override="", brightness=50, 
                       barcode=None, quantity=None, qrcode=None, arrow=None):
    """Set Static Message

    Sets the static text displayed on the device when idle.

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device.
        statics (list): A list of up to 5 static message lines (statica, staticb, staticc, staticd, statice).
        timeout (int): Default display timeout. Defaults to 5.
        location_override (str): Override for location display.
        brightness (int): Sets the standard brightness for the light with a value from 0 to 100.
        barcode (str): The string to display as a barcode without using the 'bc' prefix.
        quantity (int): The quantity to display in a box without using the 'qt' prefix.
        qrcode (str): The string to display as a QR code without using the 'qr' prefix.
        arrow (str): Direction of the arrow to display without using the 'ic' prefix.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'timeout': timeout,
        'locationoverride': location_override,
        'brightness': brightness,
    }

    # Add static lines
    for idx, line in enumerate(statics, start=1):
        payload[f'static{chr(96+idx)}'] = line  # A silly way to create 'statica', 'staticb', etc.!

    # Add new features if provided
    if barcode:
        payload['barcode'] = barcode
    if quantity is not None:
        payload['quantity'] = quantity
    if qrcode:
        payload['qrcode'] = qrcode
    if arrow:
        payload['arrow'] = arrow

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# Example usage:
# set_static_message(
#     device_id='D4E825:8B665D',
#     statics=['Welcome', 'to', 'Voodoo Robotics'],
#     barcode='abc123',
# )

# Here's a more specific use case where we display quantities.
def message_device_with_quantity(device_id, lines, quantity, time=10, sound='15,c5,4', color='r'):
    """Message Device with Quantity
    This example illustrates how to send a message to the device with a quantity display.

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device.
        lines (list): A list of up to 4 lines of text to display.
        quantity (int): The quantity to display in the box without using the 'qt' prefix.
        time (int): Number of seconds before the command times out. Defaults to 10.
        sound (str): Tune to play through the speaker. Defaults to '15,c5,4'.
        color (str): The color of the LED that lights up for the command. Defaults to 'r'.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'command': 'flash',
        'seconds': time,
        'sound': sound,
        'color': color,
        'quantity': quantity,
    }

    # Add lines to the payload
    for idx, line in enumerate(lines, start=1):
        payload[f'line{idx}'] = line

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# You can call the above with
# message_device_with_quantity(
#     device_id='D4E825:8B665D',
#     lines=['John', 'PICK', 'Red T-Shirts'],
#     quantity=35,
# )

# Just like we did with static messages, we can encode all sorts of things in commands
def message_device_fancy(device_id, command='flash', lines=None, time=10, sound='15,c5,4', color='r',
                         barcode=None, quantity=None, qrcode=None, arrow=None):
    """Message Device Fancy

    Sends a message to the device with various features like barcode, quantity, QR code, and arrow.

    Args:
        device_id (str): A unique alphanumeric string that identifies the specific Cloud Display Device.
        command (str): Specifies the light behavior for the device. Defaults to 'flash'.
        lines (list): A list of up to 5 lines of text to display.
        time (int): The amount of time that the device lights up before timing out. Defaults to 10.
        sound (str): The sound that the device makes when lighting up. Defaults to '15,c5,4'.
        color (str): The color of the LED that lights up for the command. Defaults to 'r'.
        barcode (str): The string to display as a barcode without using the 'bc' prefix.
        quantity (int): The quantity to display in a box without using the 'qt' prefix.
        qrcode (str): The string to display as a QR code without using the 'qr' prefix.
        arrow (str): Direction of the arrow to display without using the 'ic' prefix.

    Returns:
        The response from the server as a JSON object.
    """
    payload = {
        'api': '2.0',
        'command': command,
        'seconds': time,
        'sound': sound,
        'color': color,
    }

    # Add lines to the payload
    if lines:
        for idx, line in enumerate(lines, start=1):
            payload[f'line{idx}'] = line

    # Add new features if provided
    if barcode:
        payload['barcode'] = barcode
    if quantity is not None:
        payload['quantity'] = quantity
    if qrcode:
        payload['qrcode'] = qrcode
    if arrow:
        payload['arrow'] = arrow

    response = requests.post(f"{url}device/{device_id}/", headers=headers, json=payload)
    return response.json()

# Example usage:
# message_device_fancy(
#     device_id='D4E825:8B665D',
#     lines=['Item: Widget', 'Location: Aisle 3'],
#     barcode='abc123',
#     quantity=10,
#     qrcode='https://example.com',
#     arrow='topleft',
# )

# YOU SHOULD NEVER DO SOMETHING LIKE THIS
# Don't make the device light up and set static data at the same time. It can significantly slow down communications.
# response = requests.post(
#     url+"device/"+device_id+"/",
#     headers=headers,
#     json={
#         'api':'2.0',
#         'command': 'flash',
#         'line1': line1,
#         'line2': line2,
#         'line3': line3,
#         'line4': line4,
#         'line5': line5,
#         'seconds': time,
#         'sound': sound,
#         'color': color,
#         'statica': statica,
#         'staticb': staticb,
#         'staticc': staticc,
#         'staticd': staticd,
#         'statice': statice
#     },
# )
# DO NOT DO THIS!!!

# Dealing with Turbos is similar to devices
# To get a list of Turbos on Big Block, try this:
def get_turbo_list():
    """Get Turbo List

    A function that returns a list of all Turbo IDs in Big Block.

    Returns:
        A list of Turbo IDs.
    """
    response = requests.get(f"{url}turbo/", headers=headers)
    return response.json()

# turbo_list = get_turbo_list()
# input("Press Enter to see a list of all of your Turbos")
# print(json.dumps(turbo_list, indent=4))

# To get information on one specific Turbo, you can then do this:
def get_turbo_info(turbo_id):
    """Get Turbo Info
    This function retrieves all available Turbo attributes from Big Block.

    Args:
        turbo_id (str): A unique alphanumeric string that identifies the specific Turbo. 

    Returns:
        A dictionary of attributes for the specific Turbo.
    """
    response = requests.get(f"{url}turbo/{turbo_id}/", headers=headers)
    return response.json()

# turbo_info = get_turbo_info('1000000001bf8c0a')
# print(json.dumps(turbo_info, indent=4))

# OK, we saved the most useful for last:
# How about a single API call to light up multiple devices?
# Here we create an array in JSON for several commands to several devices
# Note that we use the 'devices' endpoint, not the 'device' endpoint

def send_commands_to_multiple_devices(device_ids, command='flash', lines=None, time=10, sound='15,c5,4', color='r'):
    """Send Commands to Multiple Devices
    This example function sends the same command to multiple devices with one API call.

    Args:
        device_ids (list): A list of unique alphanumeric strings that identify the devices.
        command (str): Specifies the light behavior for the devices. Defaults to 'flash'.
        lines (list): A list of up to 5 lines of text to display.
        time (int): The amount of time that the devices light up before timing out. Defaults to 10.
        sound (str): The sound that the devices make when lighting up. Defaults to '15,c5,4'.
        color (str): The color of the LED that lights up for the command. Defaults to 'r'.

    Returns:
        The response from the server as a JSON object.
    """
    commands = []
    for device_id in device_ids:
        payload = {
            'api': '2.0',
            'deviceID': device_id,
            'command': command,
            'seconds': time,
            'sound': sound,
            'color': color,
        }
        if lines:
            for idx, line in enumerate(lines, start=1):
                payload[f'line{idx}'] = line
        commands.append(payload)

    response = requests.post(f"{url}devices/", headers=headers, json=commands)
    return response.json()

# Example usage:
# device_ids = ['D4E825:8B665D', 'E8F297:F3F2F3', 'DB33A9:13897D', 'D255D3:3A17E8', 'D59094:AD433B']
# send_commands_to_multiple_devices(
#     device_ids=device_ids,
#     lines=['Hello', 'Howdy'],
# )

# The above function replaces the previous helloTimesFive function and makes it more flexible.

You May Also be Interested in:

Device Limitations

The REST API works for both our Modern Cloud Display Devices and our Classic devices.

Modern Cloud Display Devices 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 only support two lines of text.  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  bar delimiter ‘|’ 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.  Please contact us if you wish to replace your Classic devices with the new Modern devices.

Barcodes, QR Codes, and Icons

You can replace any command or static line of text on a Modern Cloud Display Device 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 Modern devices have a limitation of 26 characters per line of text, barcodes and QR Codes are limited to 23 characters (three are used for the prefix).  Note that bitly links are 22 characters — so you can encode any URL 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 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.