Skip to content
We're currently creating a lot of content. Sign up to get notified when it's ready.

Your First Plugin

This guide will walk you through the process of creating your first plugin. We will create a plugin that adds a new Panel to Qatium’s sidebar, adds a Heatmap layer of valves closed in a network and a button to close more valves.

In the process, we’ll learn how to use Qatium’s Developer Mode, which allows you to run a plugin locally and connect it to the production web app to test it. We will also use one of Qatium’s plugin templates to get started quickly.

For this guide, we will use TypeScript to write the plugin logic, and HTML and CSS to create the panel’s content.

Prerequisites

  1. A Qatium account. If you don’t have one, you can sign up for free.
  2. A recent version of Node. You can download it from nodejs.org.

Step 1: Set up your development environment

Start by creating a new plugin using the Vanilla TypeScript template:

Terminal window
npx degit qatium/plugin-sample-typescript-empty my-first-plugin

This command will create a new directory called my-first-plugin with the contents of the template.

Now, navigate to the new directory and install the dependencies:

Terminal window
cd my-first-plugin
npm install

Make sure the installation was successful by running the following command:

Terminal window
npm run dev

If all went well, you’ll see a message saying “Server running at http://localhost:8888”. Open that URL in your browser, and you’ll get a page like this:

The plugin running in your local environment

This is the plugin running in your local environment. It won’t do anything interesting until we connect it to the production Qatium web app. We’ll do that in the next step. You can close the browser tab because we won’t need it anymore.

Step 2: Connect to Qatium using Developer Mode

To connect your local plugin to the production Qatium web app, you need to enable Developer Mode in Qatium. This will load your local plugin’s code into the production web app, allowing you to test it while you develop.

To enable Developer Mode:

  • Open the Qatium web app
  • Open a network and wait for it to load
  • Open your user menu clicking in your avatar, then open the developer mode settings and click the “Activate” toggle

Enable developer mode

After a page refresh, your local plugin should be running in the sidebar. Our plugin is alive

Step 3: Build your plugin

Now that we have our plugin running in the production web app, we can edit its contents.

Follow the next steps to learn how to build a plugin from scrach. And later on, see what you can do in code by checking the API documentation.

Plugin structure: code and UI

Plugins in Qatium have two main parts: the plugin engine, where you can place calculations and interface with the network and map, and the UI code, which defines how the side panel is visualized. Learn more about how plugins run.

Both components communicate with each other through message passing, using the onMessage and sendMessage functions. In the next sections, we will learn how to do a simple plugin to communicate both parts of the plugin.

Add map visuals

We’re going to add a layer to the map to display a Heatmap of closed TCV valves. In order to do so, we’re going to edit the run event in our plugin engine.

engine/engine.ts
import { AssetStatus, OverlayLayer, PluginI, SDK, ValveFamilies } from "@qatium/plugin/engine";
export class Engine implements PluginI {
run(sdk: SDK) {
// Find closed valves
const closedValves = sdk.network.getValves((a) => (
a.family === ValveFamilies.TCV &&
!!a.simulation &&
a.simulation.status === AssetStatus.CLOSED
));
// Add a Heatmap Layer to the map with the closed valves.
sdk.map.addOverlay([
{
type: 'HeatmapLayer',
id: 'density',
data: closedValves.map((valve) => ({
geometry: valve.geometry,
properties: {},
type: 'Feature'
})),
opacity: 0.5,
visible: true,
getPosition: (j) => j.geometry.coordinates,
radiusPixels: 25
} as OverlayLayer<"HeatmapLayer">
]);
}
}

Now you should be able to see a Heatmap layer on top of the closed valves. Make sure to enable the toggle in the plugin UI to enable the plugin visualizations.

Heatmap layer

Add components to the UI

Start by adding some UI elements to the side panel. Let’s add a button to close more valves so we make our Heatmap more interesting.

ui/index.html
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="/style.css">
<script type="module" src="/main.ts"></script>
</head>
<body>
<h1>Close TCV valves</h1>
<button id="close-valves">Close 10 valves</button>
</body>
</html>

After reloading Qatium, the side panel will look like this

Plugin panel after saving the changes

Connect the button with the plugin

Clicking the new Close 10 valves button we have created has no effect. To add functionality to it, we will send a message from the UI to the plugin instructing the plugin to close 10 valves.

To do so, send the message from the UI to the engine.

ui/main.ts
import { sendMessage } from '@qatium/plugin/ui'
const changeMapButton = document.querySelector("#close-valves") as HTMLButtonElement;
changeMapButton?.addEventListener("click", () => {
sendMessage({ command: "closeValves", data: 10 })
});

Then, the plugin will have to react to this message. To do this, use the onMessage event from the Events API.

engine/engine.ts
// We need to pass the message type so typescript know what we're sending from the UI
type Message = { command: string; data: number }
export class Engine implements PluginI<Message> {
// ...previous code
private closeValves(sdk: SDK, quantity: number) {
return sdk.network
.getValves((valve) => valve.simulation?.status === "OPEN")
.slice(0, quantity)
.forEach((valve) => {
sdk.network.setStatus(valve.id, "CLOSED");
});
}
onMessage(sdk: SDK, message: Message) {
switch (message.command) {
case "closeValves":
return this.closeValves(sdk, message.data)
default:
return;
}
}
}

After reloading Qatium and clicking the button, we will see more valves closed and the Heatmap updated.

Communicate the plugin with the UI

In the previous points, we have seen how to communicate with the plugin when the UI initiates an action. But in many instances, our plugin will have to react to changes in the network model, map, etc.

To do so, subscribe to these changes using the Events API and send a message to the UI to reflect the changes.

Edit the run() method, which is called everytime the app’s state changes, and send the number of closed valves to the UI.

engine/engine.ts
run(sdk: SDK) {
...
sdk.ui.sendMessage(closedValves.length);
}

At the UI side, first add a placeholder in the HTML page.

ui/index.html
<h1>Close TCV valves</h1>
<p id="valves">Loading...</p>
<button id="close-valves">Close 10 valves</button>

Then subscribe to messages coming from the plugin, and update the placeholder with the number of closed valves.

ui/main.ts
import { onMessage } from '@qatium/plugin/ui'
onMessage<number>((msg) => {
const valveText = document.querySelector("#valves");
if (!valveText) return;
valveText.innerHTML = `${msg}`;
})

Plugin panel with the number of valves