Appendix

Script Widget

This widget enables the definition and execution of Flight Operational Procedures (FOPs) through customizable JavaScript-based scripts. It provides a visual terminal, parameter inputs, confirmation dialogs, and interaction with onboard telemetry and telecommands.

The script widget allows:

  • Upload and execute scripts.
  • Send telecommands (TC) to the platform.
  • Read telemetry from the platform.
  • Show interactive dialogs for operator input or confirmation.
  • Add logic (IF, WHILE, FOR) to control the script flow.
  • Wait between actions.
  • Check ACKs after sending commands.
  • Evaluate telemetry conditions to continue or abort.
  • Show real-time progress via the terminal.
  • Apply transformations (scaling, offset, polynomial) to command data.
  • Abort execution.
  • Read a .json file and transform it in an object available in the script.
  • Upload custom files, called Script Libraries, with functions or constants to be available globally in the scripts.
  • Syntax errors will be shown when the editing area is unfocused, or in the terminal when executing an incorrect script.

Scripts are written in JavaScript, using an asynchronous context, and executed in the widget sandbox. Therefore, for instance, write comments with //, and all the well-known syntax of this language.

Getting Started

  1. Open the widget script configuration panel.
  2. Use the file picker to upload a file or directly write in the code text area.
  3. Edit or review the script in the editor.
  4. Click Save button.
  5. Click Run to start execution.
  6. The script will execute from top to bottom asynchronously.
  7. A spinner indicates that the script is running.
  8. There is the option to stop the script execution.

Importing custom libraries

It is possible to import custom libraries. To achieve this, navigate to SetupScript Libraries. The export / import must not be used. Just declare the functions with a regular declaration (keywork function) and the variables with const/let. They will be available in all the scripts. This is not supposed to be used for external libraries not supported by native JavaScript.

The data from the libraries would be available in the scripts at the start of the execution. This means that, if there is currently a script running, the libraries will not be available until the next execution.

Libraries are evaluated in chronological order, meaning that is required to use a function or variable from another library, it must be imported before the one that uses it.

This is an example of the dialog of configuration (it is possible to rename the modules):

Script - Configuration

Stopping a Script

The execution can be stopped:

  • Automatically from within the script (adding a return; clause). This is a graceful end.
  • Force the script to abort at any point using throwAbort().
  • With the stop button. This is also a forced termination.

Available Scripting Functions

The script runs in a safe sandbox environment and offers the following helper functions available:

Function Description
getVar(typeId: number, id: number): Promise<number> Reads a telemetry variable (with Type ID*, and ID** indicated) from the UAV. Returns the value, or -1 if not available.
commandVars(varList: SystemVarData[], values: number[]): Promise<void> Sends telecommands (TCs) with the given values. If the promise is successfully resolved, it means that the command has been properly received. If an error occurs, an Error will be thrown or the promise will be rejected.
openPopUpInput(title: string, subtitle: string[], inputData: Array<InputData>): Promise<Array<number \| null>> Opens a dialog to request user input (numbers or select) with validations.
confirmationDialog(title: string, messages: string[]): Promise<boolean> Opens a confirmation dialog for user confirmation.
openJsonInput(title: string): Promise<object> Opens a dialog to select a .json file, and returns the object contained in that file. If the format is invalid, the returned object would be null and the error would be shown.
println(message: string, type?: TerminalMsgType): void Logs the message in the terminal panel.
log(message: string, type?: LogType): void Logs the message in the log system file.
sleep(milliseconds: number): Promise<void> Pauses the script for the given time.
clear(): void Clears the terminal output.
throwAbort(): void Forces the finish of the script.


SystemVarData interface:

interface SystemVarData {
    id: number;
    typeId: number;
}
InputData interface:

interface SelectOption {
  label: string;
  value: number;
}

type InputType = 'number' | 'select';

interface InputData {
  title: string;
  type?: InputType; // defaults to 'number'
  defaultValue?: number;
  min: number | null; // only needed if type === 'number'
  max: number | null;
  options?: SelectOption[]; // compulsory if type === 'select'
}

TerminalMsgType enum:

Type Value expected (strings)
Default No value needed
Error error
Warning warning
Success success

LogType enum:

Type Value expected (strings)
Default No value needed.
Error error
Warning warning

Variable types (typeId):

Type ID
Real 0
Integer 2
Bit 3


The ID of the variable can be found in the dialog of variable selection in the application. Example:

Variable ID

The ID in this case would be 3110 (and type Real, then typeId is 0).

Example Use Cases

  1. 🛰️ Get the Current Phase

To read the current phase:

const phase = await getVar(2, 1); // Type ID 2 = Integer, ID 1 = Phase
println("Current phase: " + phase);
  1. 📥 Get Input from Operator (Popup Form)

To request user-entered values (two first are input number type, and the third one is a select list):

const result = await openPopUpInput("Enter Mission Parameters", [
    "Mode options:",
    "Passive > 0",
    "Position > 1"
  ],[
  {
    title: "Speed (m/s)",
    defaultValue: 10,
    min: 0,
    max: 100
  },
  {
    title: "Altitude (m)",
    defaultValue: 30,
    min: 10,
    max: 300
  },
  {
    title: 'Mode',
    type: 'select',
    defaultValue: 1,
    options: [
      { label: "Passive", value: 0 },
      { label: "Position", value: 1 }
    ],
  }
]);

if (result) {
  println("Speed: " + result[0] + ", Altitude: " + result[1] + ", Mode: " + result[2]);
} else {
  println("No input provided.");
}

Note

All fields are validated. The user cannot continue if values are out of range. If the limits are not provided, then no validation would take effect. If the default value is not changed, then that value would be sent.

  1. 📋 Print to the Terminal

To show status/progress updates:

println("Step 1: Initialization ✅");
println("Waiting for confirmation...");
  1. 🧹 Clear the Terminal
clear(); 

Removes all previous logs.

  1. Add a Pause

To wait two seconds before continuing:

await sleep(2_000);

The number is in milliseconds.

  1. Confirmation Dialog

To confirm with the operator before continuing:

const confirmed = await confirmationDialog("Continue?", ["This will start the mission."]);
if (!confirmed) {
  println("Aborted by operator ❌");
  return;
}
  1. 📡 Send Telecommands (TC)

To command variables to the UAV:

await commandVars([
  { typeId: 0, id: 3110 }, 
  { typeId: 0, id: 3111 }  
], [15, 50]); // Values to send

Multiple variables can be sent in one call.

  1. Verify ACK

To confirm a telecommand was acknowledged correctly:

try {
  await commandVars([{ typeId: 0, id: 3110 }], [15]);
  println("ACK OK ✅");
} catch (e) {
  await println(e);
}

Retries may also be performed inside a loop.

for (let i = 0; i < 3; i++) {
  try {
    await commandVars([{ typeId: 0, id: 3110 }], [15]);
    await println("Sent successfully!");
    break;
  } catch (err) {
    await println("Retry " + i + ' ' + err);
    await sleep(500);    
  }
}
  1. 📊 Check Specific Telemetry

Telemetry conditions can be verified:

const battery = await getVar(0, 2002);
if (battery < 11) {
  println("Battery too low (" + battery + "V) ❌");
  return;
}

Useful for safety conditions.

  1. 🔁 Conditional Execution (IF / WHILE / FOR)

Standard JavaScript control structures are supported:

if (await getVar(0, 1000) > 50) {
  println("Condition met, sending TC");
  await commandVars([...], [...]);
}

while (await getVar(0, 3110) !== 15) {
  println("Waiting for ACK...");
  await sleep(500);
}
  1. Transform Values Before Sending (Transfer Functions)

Values can be modified before sending:

const rawSpeed = 10;
const scaledSpeed = rawSpeed * 1.5 + 2;
await commandVars([{ typeId: 0, id: 3110 }], [scaledSpeed]);

Apply any function, including polynomials:

function polynomialTransform(x) {
  return 0.01 * x ** 2 + 3 * x + 7;
}
await commandVars([{ typeId: 0, id: 3110 }], [polynomialTransform(20)]);
  1. 📌 Checklist and Step Progress

Use println() to simulate checklist status:

println("✅ Step 1: Pre-flight check passed");
println("✅ Step 2: Command sent");
println("❌ Step 3: ACK not received");
  1. 🚩 Colored outputs

Use println('This is an error message', 'error') and the other types to see a different color in the output console:

Colored outputs

Note

The type of colored output depends on the widget configuration (color mode).

  1. 🎨 Create custom functions

A comfortable way of working with this widget is to create custom functions. An example of this:

async function setAltitude(value) {
  return await commandVars([{ typeId: 0, id: 3110 }], [value]);
}

The only thing that needs to be done is:

await setAltitude(55);

And this will set the user variable 10 to 55. If it is necessary to go a bit further, this values can be stated as constants at the start of the script:

const set_altitude_cmd = {
  id: 3110,
  typeId: 0
}

async function setAltitude(value) {
  await commandVars([set_altitude_cmd], [value]);
}
  1. 🔨 Upload a JSON and get an object

Use openJsonInput('Select the JSON with the altitude variables') to read a JSON file and get the corresponding object. This file:

JSON Upload & Validation

Can be read with this code:

const json = await openJsonInput('Select the JSON with the altitude variables');
const msl = json?.msl;
if (typeof msl !== 'number') {
  println('Missing position.msl in JSON ❌', 'warning');
  return;
}
println('Altitude (MSL): ' + msl);

We strongly recommend validating the structure of the JSON object (optional chaining and/or typeof).

  1. 💼 Using libraries

After having imported the needed custom libraries, all the functions should be available in the script. No import should be used (as it should not be used any export).

Assuming a library defines the following code:

function doublePrint(value) {
  println(value);
  println(value);
}

The script could look like this:

const x = 2;
const y = 5;
const result = x * y;
doublePrint('The result is ' + result);

The terminal should show the following output:

The result is 10
The result is 10
  1. 📚 Creating enumerations

The way of creating enumerations we strongly recommend is the following (a native feature of JavaScript):

const Position = Object.freeze({
  TOP: 0,
  RIGHT: 1, 
  BOTTOM: 2, 
  LEFT: 3
})

Using it is as easy as entering Position.RIGHT, which returns the value 3. The same can be done with strings:

const State = Object.freeze({
  ON: 'on',
  OFF: 'off'
})

In this case, the return value would be a string. Although, the use of Object.freeze is optional, its use is encouraged as it guarantees that the value of the enumeration does not change and that no new values are added, which is the expected behavior of an Enumeration.

Real use example:

const Vars = Object.freeze({
  PHASE: 1
})

const phase = await getVar(2, Vars.PHASE);
println('Current phase is: ' + phase);

Send a telecommand with custom enumeration:

// This code could be in a library called Constants.js
const Position = Object.freeze({
  PASSIVE: Object.freeze({
    typeId: 0,
    id: 3110
  }),
  POSITION: Object.freeze({
    typeId: 0,
    id: 3111
  })
})

// This function could be in a library called RadFns.js
async function setRadPos(pos, value) {
  return await commandVars([pos], [value])
}

// Main script
await setRadPos(Position.PASSIVE, 100)
  1. 📂 Using system logs

The log function enables direct writing into the system log, allowing for the specification of different severity levels.

One example of how it could be used is as follows:

log('Example of log')
log('This case is a waning', 'warning')
log('And an error!!!', 'error')

With a result in the file log like this:

[2025-09-01 09:55:55.931] [info]  Example of log
[2025-09-01 09:55:55.932] [warn]  This case is a waning
[2025-09-01 09:55:55.933] [error] And an error!!!

The format is clear: [date time] [type] Text message. Using this as a starting point, a good idea would be to create a custom sub-format. For instance, something like [script] [operation] status/result/info. The final result could be something like this:

[2025-09-01 10:01:01.101] [info] [script] [setRadPos] Position.ABSOLUTE 100 

This makes it easier to filter by operation or script logs. This can be achieved with the following code:

const Position = Object.freeze({
  ABSOLUTE: 0,
  RELATIVE: 1
})

log(`[script] [setRadPos] Position.ABSOLUTE 100`)

// Or parameterizing it
function logOperation(op, variable, value) {
  return `[script] [${op}] ${variable} ${value}`
}

log(logOperation('setRadPos', 'Position.ABSOLUTE', 100))

Error list

The most common errors that could appear while executing the script are:

  • No platform found. Currently it is not possible to play a script without a platform selected.
  • The message functionName argument must be string/number/array. Defined types must be respected.
  • An error while reading a JSON file because of the format of the uploaded file. Please, do respect the standard. It would say something like Error reading JSON file: ....
  • commandVars function will be rejected if it could not be completed. For instance, it could throw any of this errors:

    • A timeout because the connection with the platform is lost.
    • Too many fields sent.
    • Trying to write a read-only variable.
    • Any other reason of this kind.
  • Unknown operation when executing a non supported operation.

  • In the event of a syntax error, it will be displayed as Error: followed by the reason provided by the language.

Notes and Tips

  • The use of await may be required for asynchronous functions: getVar, sleep, commandVars, openPopUpInput, confirmationDialog. Remember JavaScript is an asynchronous and single-threaded language.
  • println() shows logs in the terminal. It is the primary tool for guiding the operator.
  • To have the possibility of comfortably using the stop button, sleep functions and dialogs are recommended.

Safety Notes

  • Scripts are sandboxed and run asynchronously.
  • The functions provided are limited due to safety reasons.
  • Be sure to validate user inputs and telemetry ranges.
  • Bear in mind a forced stop could left the system on a non-desired state.

📎 Summary Table

Feature Supported
Load and edit script from file
Run execution
Send telecommands
Wait between commands
Operator input/select popup
Confirmation dialogs
ACK verification
Telemetry check ✅ (via getVar)
Conditional execution
Checklist progress (via logs)
Stop execution
Read a JSON and get object
Import custom libraries


For any further help writing scripts, refer to the examples above or contact support.


© 2026 Embention. All rights reserved.