Skip to content

Creating a Python Plugin Tutorial

lucaszw edited this page Feb 14, 2018 · 6 revisions

Requirements

Optional

Installing Dependencies

Install MicroDrop

npm i --global microdrop
microdrop-3
#launching microdrop { HTTP_PORT: 3000, MQTT_PORT: 1883 }
#Launch Jupyterlab (complete)
#  or visit localhost:3000 (no filebrowser, terminal, or notebooks)

Install MicroDrop Python Client

pip install microdrop_client

Install Jupyterlab

# if you use conda:
conda install jupyterlab

# if you use pip:
pip install jupyterlab
jupyter serverextension enable --py jupyterlab --sys-prefix

Create a new project

mkdir my_python_plugin
cd my_python_plugin
jupyter lab
# A jupyterlab environment should open in your web browser

Introduction to Python 3.5's asyncio library

You might have been hearing more and more about asynchronous programming as of late thanks to Python 3.5, and NodeJS 8 . Python's asyncio library helps with the handling of waiting on requests from communication protocols like http or mqtt. Normally, this would be done either through callbacks (passing functions as parameters called upon a message being received), or by blocking execution of your code through a while loop. Instead Python 3 introduces Futures: an object that is resolved later on, and who's value can be awaited upon when necessary.

For more information, I recommend checking out https://www.blog.pythonlibrary.org/2016/07/26/python-3-an-intro-to-asyncio/.

How does this effect writing recipes for microdrop?

Your recipe/protocol needs to be written using python 3's new helper syntax for asynchronous methods. If you want to write your recipes without callbacks, you can use microdrops 'execute' method, that expects an asynchronous function (which uses the 'async' decoration keyword), with each asynchronous operation (such as those from microdrop_client) prefixed with the 'await' keyword.

async def my_recipe(microdrop, args):
    plugin = 'some-microdrop-plugin'
    action = 'some-action'
    data = await microdrop.trigger_plugin(plugin, action, {})
    return data

microdrop.execute(my_recipe, args)

Import microdrop_client

Open a new Python 3 Jupyter Notebook and enter the following into a new cell

from microdrop_client import MicroDrop
microdrop = MicroDrop()
wait_for = microdrop.loop.run_until_complete

Opening the microdrop UI

Navigate to localhost:3000/plugin-manager, and enable all the plugins. Navigate to localhost:3000/display (should now see a view for the fluxel device)

Turning on an electrode

Awesome! Now we can both see the state of our fluxel device, and control it through a jupyter notebook. Lets toggle on a few electrodes.

  1. First lets get a list of subscriptions we can publish to for electrodes-model:
subs = wait_for(microdrop.get_subscriptions('electrodes-model'))
print(subs)
#['microdrop/put/electrodes-model/active-electrodes', 'microdrop/trigger/electrodes-model/toggle-electrode', #'microdrop/web-server/signal/running-state-requested', 'microdrop/trigger/electrodes-model/get-subscriptions']
  1. It looks like we want to publish to 'electrodes-model/active-electrodes'. The microdrop_client contains helper functions to make this more simple. Although you will need to be a bit familiar with Python 3's asyncio library. Enter the following into another cell:
electrodes = ['electrode053', 'electrode087','electrode052','electrode049','electrode051','electrode084','electrode048','electrode004','electrode005']

async def toggle_electrode(microdrop, id):
    plugin = 'electrodes-model'
    action = 'toggle-electrode'
    await microdrop.trigger_plugin(plugin, action, {'electrodeId': id, 'state': True} )

for electrode in electrodes:
    microdrop.execute(toggle_electrode, electrode)

You can validate this either through seeing a smiley face on the fluxel device view, or by running:

wait_for(microdrop.get_state('electrodes-model', 'active-electrodes'))
#['electrode053',
# 'electrode087',
# 'electrode052',
# 'electrode049',
# 'electrode051',
# 'electrode084',
# 'electrode048',
# 'electrode004',
# 'electrode005']

Draw routes

Next lets draw routes programmatically and execute them.

  1. Turn off all the electrodes
wait_for(microdrop.put_plugin('electrodes-model', 'active-electrodes', []))
  1. Add route to the fluxel device
path = ['down', 'down', 'right', 'right', 'up', 'up']
start = 'electrode065'
r = {'start': start, 'path': path }
wait_for(microdrop.put_plugin('routes-model', 'route', r))
  1. Execute all the routes on the fluxel device
routes = wait_for(microdrop.get_state('routes-model', 'routes'))
wait_for(microdrop.trigger_plugin('routes-model', 'execute', {'routes': routes}))

Jupyter Notebook

Clone this wiki locally