IvoryOS plugin template
This repository is a starter template for building plugin pages for IvoryOS.
Plugins are Flask Blueprint objects that IvoryOS loads as additional UI pages. Use a plugin when you want to add a monitoring page, visualization, dashboard, custom control panel, analysis view, or any other page that should live alongside the core IvoryOS workflow UI.
Features
Adds standalone pages to IvoryOS through Flask blueprints.
Can reuse the IvoryOS base template when running inside IvoryOS.
Can run as a standalone Flask app during development.
Can optionally attach to the shared Flask-SocketIO server.
Can access the active IvoryOS deck and runtime state when loaded inside IvoryOS.
Repository structure
ivoryos_plugin/
|-- templates/
| `-- example.html
|-- __init__.py
`-- plugin.py
README.md
pyproject.toml
requirements.txt
setup.py
Quick start
Clone the template and install IvoryOS:
git clone https://gitlab.com/heingroup/ivoryos-suite/ivoryos-plugin-template
cd ivoryos-plugin-template
pip install ivoryos
For local plugin development, install the template in editable mode:
pip install -e .
Register the plugin with IvoryOS
Import the plugin blueprint and pass it to ivoryos.run(...):
from ivoryos_plugin.plugin import plugin
import ivoryos
ivoryos.run(__name__, blueprint_plugins=plugin)
Multiple plugins can be registered as a list:
ivoryos.run(__name__, blueprint_plugins=[plugin, another_plugin])
IvoryOS registers each plugin under:
/ivoryos/<blueprint_name>
For the default Blueprint("plugin", ...), the plugin page is available at:
/ivoryos/plugin
Minimal plugin.py
Each plugin must expose a Flask Blueprint. The blueprint name must be unique and should not collide with IvoryOS built-in blueprints such as auth, control, data, design, execute, library, or main.
import os
from flask import Blueprint, current_app, render_template
plugin = Blueprint(
"plugin",
__name__,
template_folder=os.path.join(os.path.dirname(__file__), "templates"),
)
@plugin.route("/")
def main():
base_exists = "base.html" in current_app.jinja_loader.list_templates()
return render_template("example.html", base_exists=base_exists)
Template pattern
The example template can extend the IvoryOS base template when the plugin is running inside IvoryOS, and fall back to a complete HTML page when running standalone.
{% if base_exists %}
{% extends "base.html" %}
{% block title %}Plugin{% endblock %}
{% else %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Plugin</title>
</head>
<body>
{% endif %}
{% block body %}
<div class="container mt-4">
<h1>IvoryOS plugin page</h1>
</div>
{% endblock %}
{% if not base_exists %}
</body>
</html>
{% endif %}
Access the active IvoryOS deck
When the plugin is loaded inside IvoryOS, the active deck is available from the shared runtime state.
from ivoryos import global_state
@plugin.route("/status")
def status():
deck = global_state.deck
if deck is None:
return {"online": False, "message": "No active deck"}
return {
"online": True,
"deck": getattr(deck, "__name__", "deck"),
}
Access hardware defensively. A plugin can be opened before a specific instrument has been initialized.
deck = global_state.deck
balance = getattr(deck, "balance", None) if deck else None
Optional Socket.IO integration
If your plugin needs websocket events, define an init_socketio(...) function and attach it to the blueprint object. IvoryOS calls this function when loading the plugin.
socketio = None
def init_socketio(sio):
global socketio
socketio = sio
@socketio.on("plugin_ping")
def handle_plugin_ping(data):
socketio.emit("plugin_pong", data)
plugin.init_socketio = init_socketio
Run standalone during development
The plugin can run outside IvoryOS for layout and route development.
from flask import Flask
if __name__ == "__main__":
app = Flask(__name__)
app.register_blueprint(plugin)
app.run(debug=True)
For standalone Socket.IO development:
from flask import Flask
from flask_socketio import SocketIO
if __name__ == "__main__":
app = Flask(__name__)
app.register_blueprint(plugin)
socketio = SocketIO(app)
init_socketio(socketio)
socketio.run(app, debug=True, allow_unsafe_werkzeug=True)
Development prompts
This template works well with coding assistants. Example prompts:
“Build an IvoryOS plugin page that shows a live webcam stream.”
“Convert this existing HTML page into an IvoryOS plugin template.”
“Add a Socket.IO event to stream instrument status into the plugin page.”
“Add a route that reads the current IvoryOS deck and shows the state of
deck.balance.”
Notes
Keep the blueprint name unique.
Keep plugin routes relative to the plugin blueprint.
Avoid assuming hardware is connected; check
global_state.deckand instrument attributes first.Keep reusable plugin logic inside the plugin package instead of editing IvoryOS core files.