THE EASIEST
Pick-to-Light REST Integration
Simple! The way it should be.
THE EASIEST
Simple! The way it should be.
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.
This is a playlist of REST API tutorials on our Voodoo Robotics YouTube Channel
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.
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')
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.
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.
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.