Skip to content

Custom Menu

A Custom Menu is a menu entry defined at space level. It links to an existing Custom HTML page or Animation, or acts as a group that holds child menus.

In the data model CustomMenu extends Menu, which means every custom menu is also a menu privilege. The two share a single UUID: custom_menu_id and menu_id are always the same value. This detail governs how visibility is configured.

Custom Menus belong to a space, not to a project. The relationship to a project is indirect, established through the project that owns the linked customHtmlId or animationId.

The Custom Menus screen — root-level list, with the second- and third-level panels below it

FieldTypeRequiredDescription
nameString (≤100)YesMenu name. Cannot be changed after creation
contentTypeEnumYesHtml, Animation or Group. Cannot be changed
iconString (≤16384)NoIcon descriptor JSON (see below)
menuOrderIntegerNoOrdering within the menu
parentCustomMenuNoParent menu — used for nesting
customHtmlIdStringYes for HtmlCustom HTML record to link. Cannot be changed
animationIdStringYes for AnimationAnimation record to link. Cannot be changed
animationParamsString (≤32767)NoJSON parameters passed to the animation

The new-menu form asks for the common fields:

The new Custom Menu form — Name, Order, Content Type and Icon fields

The edit form — the fields look editable, but only Order and Icon persist

The ContentType enum has three values:

TypeRequired fieldDescription
HtmlcustomHtmlIdOpens an existing Custom HTML page. The same HTML can be linked from several menus
AnimationanimationIdOpens an existing Animation. Parameters can be passed via animationParams
Group—An empty container that holds child menus

The form’s own list offers exactly these three:

The Content Type list open — Animation, Html and Group

The Icon field on the menu form previews the selected icon. While none is set, Choose icon opens the library; once an icon is assigned the buttons become Change and Clear (the edit form pictured above shows that state).

The Icon field on the form — preview box and the Choose icon button

The library offers four sources — FontAwesome, OpenBridge, Material Symbols and Custom — with search by name and a category filter.

The icon library — FontAwesome, OpenBridge, Material Symbols and Custom tabs, with search and a category filter

The chosen icon is stored as a descriptor JSON made up of vector paths only; it carries no colour information. This lets the icon inherit the surrounding text colour and render correctly in both light and dark themes.

Custom Menus can be nested three levels deep at most. Each level has its own endpoint:

Root (level 1) POST /api/custom-menus Second (level 2) POST /api/custom-menus/{root}/second / Third (level 3) POST /api/custom-menus/{root}/second/{second}/third Root (level 1) POST /api/custom-menus Second (level 2) POST /api/custom-menus/{root}/second Third (level 3) POST /api/custom-menus/{root}/second/{second}/third

Attempting to create a fourth level returns an error. The same level-specific logic applies to updates and deletions.

A typical arrangement:

Plant (Group, root) Pump Station (Html, level 2) / Inverters (Group, level 2) / Live Schematic (Animation, level 3) / Daily Report (Html, level 2) Plant (Group, root) Pump Station (Html, level 2) Inverters (Group, level 2) Live Schematic (Animation, level 3) Daily Report (Html, level 2)

Each level has its own panel on screen. Selecting a root row lists that menu’s children in the second-level panel; selecting a row there in turn fills the third-level panel.

A root menu selected — the second-level panel lists the Html menu nested under it

In JDK 21 a Custom HTML page is a separate record. The menu does not hold the content itself; it only references an existing HTML record. The order is:

  1. Create the Custom HTML page → obtain its customHtmlId
  2. Create a Custom Menu of type Html and pass that ID

The Custom HTML list — the pages a menu can be bound to are managed here

At runtime the menu opens with the linked page embedded in it:

Runtime — the menu under the Plant group with its Custom HTML page embedded

A Custom HTML page runs inside a sandboxed iframe. The only way to reach the inSCADA API is the InscadaApi proxy that the server injects into the page automatically:

var api = new InscadaApi(); // the page resolves its own project
(async function () {
var v = await api.getVariableValue('ActivePower_kW');
document.getElementById('power').textContent = v.value.toFixed(1);
// Several variables — a single round trip
var batch = await api.getVariableValues(['Voltage_V', 'Current_A']);
console.log(batch.Voltage_V.value);
})();

Adding it directly to the administrator role is not possible either: Administrator and Space Admin are system roles and cannot be modified; attempting to update them returns an error.

Because privileges are additive, the solution is to create a second role that carries only the relevant custom menus:

  1. Create a role whose name reflects its purpose — for example PumpStationAccess
  2. Add the new menu to that role
  3. Assign the role to the relevant users — their existing roles are kept, the new one is added on top

For subsequent menus you only need to repeat step 2.

Following least privilege, add only custom menus to this role. System menus such as Home, Variables and Trends already come from the user’s administrator role; adding them again clutters the audit trail for no benefit.

  1. List the user’s roles
  2. List the menus bound to each of those roles
  3. If the menu appears in none of them, the role-menu binding is missing

An existing menu can be copied under a new name. Since name is immutable, this is the practical way to produce a similar menu with a different name.

Deleting a Custom Menu also deletes the menu privilege behind it, and every role binding for that menu is cleaned up automatically. No dangling references are left on the roles.

Bulk deletion is level-specific: all the menus being deleted must sit at the same level (root / second / third). Use separate operations for menus at different levels.