Create alarm from map context menu
This example adds a Create alarm here action in the map context menu. The menu reads longitude and latitude from the click event, opens a form to edit alarm properties, then executes maps.actions.run_script with script.create_alarm.
git checkout origin/osp-alarms-configuration .
git checkout origin/osp-web-configuration .
git checkout origin/osp-maps-configuration .
git checkout origin/osp-variables-configuration .
git checkout origin/osp-scripts-configuration .
git checkout origin/example-maps-create-alarm .
Learning goals
Add a map context menu entry that captures the clicked coordinates.
Open a form prefilled from the map click.
Execute a script to create an alarm and report the result in the UI.
Configuration structure
Steps
1. Add rights for map and script folders
The example ships rights files for both top-level folders:
{
"moduleId": "modules.rights.rights",
"write": {
"override": [
"all"
]
},
"read": {
"override": [
"all"
]
}
}
{
"moduleId": "modules.rights.rights",
"write": {
"override": [
"all"
]
},
"read": {
"override": [
"all"
]
}
}
2. Configure the context menu action
The menu extracts click coordinates, prompts a form, and triggers maps.actions.run_script.
{
"moduleId": [
"modules.web.web-1"
],
"name": "Maps create alarm menu",
"description": "Create an alarm from map context menu and clicked position",
"menus": [
{
"label": "Create alarm here",
"icon": "add_alert",
"context": [
{
"type": [
"Maps"
],
"scopes": [
"Click"
],
"action": "root.maps.actions.run_script",
"input": {
"scriptId": {
"expression": "'root.script.create_alarm'"
},
"longitude": {
"extract": "root.map.point.lngLat.lng",
"as": "decimal"
},
"latitude": {
"extract": "root.map.point.lngLat.lat",
"as": "decimal"
},
"form": {
"prompt": {
"type": "FORM",
"formId": "root.maps.create_alarm.form",
"formSave": false,
"labels": {
"title": "Create alarm",
"message": "Confirm and edit the alarm fields",
"save": "Create",
"cancel": "Cancel"
},
"data": {
"summary": {
"expression": "'Alarm from map click'"
},
"source": {
"expression": "'maps.context-menu'"
},
"location": {
"expression": "'lng=' + map.point.lngLat.lng.toFixed(6) + ', lat=' + map.point.lngLat.lat.toFixed(6)"
},
"severity": {
"expression": "300"
},
"tags": {
"expression": "'maps,manual'"
}
}
}
}
},
"output": [
{
"operation": "snackbar",
"input": {
"message": {
"expression": "${result.success} ? 'Alarm created from map context menu' : 'Alarm creation failed from map context menu: ' + (${result.message} || ${result.error} || 'unknown error')"
},
"variant": {
"expression": "${result.success} ? 'success' : 'error'"
}
}
}
]
}
]
}
]
}
3. Configure the action called by the menu
The action type is RUN_SCRIPT and delegates execution to the Scripts module.
{
"moduleId": [
"modules.web.web-1"
],
"type": "RUN_SCRIPT"
}
4. Configure the form and schema
The prompt form lets users edit alarm properties before script execution.
{
"moduleId": [
"modules.web.web-1"
],
"name": "Map create alarm form",
"description": "",
"bindings": {},
"rights": [],
"schema": "root.maps.create_alarm.form",
"ui": {
"type": "VerticalLayout",
"elements": [
{
"type": "Control",
"label": "Summary",
"scope": "#/properties/summary"
},
{
"type": "Control",
"label": "Source",
"scope": "#/properties/source"
},
{
"type": "Control",
"label": "Location",
"scope": "#/properties/location",
"options": {
"readonly": true
}
},
{
"type": "Control",
"label": "Severity",
"scope": "#/properties/severity"
},
{
"type": "Control",
"label": "Tags",
"scope": "#/properties/tags"
},
{
"type": "Control",
"label": "Serial (optional)",
"scope": "#/properties/serial"
}
]
},
"initialValue": {
"source": "maps.context-menu",
"severity": 300,
"tags": "maps,manual"
},
"submit": {
"destination": "Request",
"type": "Deferred"
}
}
{
"schema": {
"type": "object",
"properties": {
"summary": {
"type": "string"
},
"source": {
"type": "string"
},
"location": {
"type": "string"
},
"severity": {
"type": "number"
},
"tags": {
"type": "string"
},
"serial": {
"type": "string"
}
}
}
}
{
"moduleId": "modules.web.web-1",
"name": "Map create alarm form schema",
"views": []
}
5. Configure the script declaration and implementation
detached.scripts declares the script and points to its source file.
{
"moduleId": "modules.scripts.scripts-1",
"accessedValues": [],
"scheduledExecutions": [],
"sourceFile": "root/script/create_alarm/create_alarm.js"
}
The script validates map coordinates, maps form values, and creates the alarm.
const DEFAULT_SEVERITY = 300;
try {
main();
} catch (error) {
const message = !!error && !!error.message ? String(error.message) : String(error);
log.error("Map create alarm script failed with unexpected error [{}].", message);
execution.reportFailure(message);
}
function main() {
const parameters = Array.isArray(trigger.parameters) && trigger.parameters.length > 0
? trigger.parameters[0]
: {};
const form = typeof parameters.form === "object" && parameters.form !== null ? parameters.form : {};
const longitude = Number(parameters.longitude);
const latitude = Number(parameters.latitude);
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) {
log.error("Missing map coordinates in script parameters [{}].", JSON.stringify(parameters));
execution.reportFailure("Missing map coordinates in script parameters.");
}
const severity = parseSeverity(form.severity);
const summary = toText(form.summary, "Alarm from map click");
const source = toText(form.source, "maps.context-menu");
const location = toText(form.location, `lng=${longitude.toFixed(6)}, lat=${latitude.toFixed(6)}`);
const serial = buildSerial(form.serial);
const tags = parseTags(form.tags);
log.info(
"Create alarm from map context menu with serial [{}], severity [{}], source [{}], location [{}].",
serial,
severity,
source,
location
);
const alarm = alarms.newAlarmBuilder(serial, severity)
.setSummary(summary)
.setSource(source)
.setLocation(location)
.addTags(tags)
.build();
const createResult = alarms.create(alarm);
if (createResult && createResult.success === true) {
log.info("Alarm creation succeeded from script [{}] with create().", serial);
execution.reportResult("Alarm created", {
serial: serial,
longitude: longitude,
latitude: latitude
});
}
log.warn(
"Alarm create() failed for script [{}], trying insert(). create result [{}].",
serial,
JSON.stringify(createResult)
);
const insertResult = alarms.insert(alarm);
if (!insertResult || insertResult.success !== true) {
const createMessage = !!createResult && !!createResult.message ? createResult.message : JSON.stringify(createResult);
const insertMessage = !!insertResult && !!insertResult.message ? insertResult.message : JSON.stringify(insertResult);
const failureMessage = `create failed [${createMessage}] / insert failed [${insertMessage}]`;
log.error("Alarm creation failed from script [{}] cause [{}].", serial, failureMessage);
execution.reportFailure(failureMessage);
}
log.info("Alarm creation succeeded from script [{}] with insert().", serial);
execution.reportResult("Alarm created", {
serial: serial,
longitude: longitude,
latitude: latitude
});
}
function toText(value, fallback) {
if (value === undefined || value === null) {
return fallback;
}
const normalized = String(value).trim();
return normalized.length > 0 ? normalized : fallback;
}
function parseSeverity(rawValue) {
const parsed = Number(rawValue);
return Number.isFinite(parsed) ? parsed : DEFAULT_SEVERITY;
}
function buildSerial(serialValue) {
const provided = toText(serialValue, "");
if (provided.length > 0) {
return provided;
}
return `maps-${Date.now()}-${Math.floor(Math.random() * 1000000)}`;
}
function parseTags(rawTags) {
if (Array.isArray(rawTags)) {
return rawTags
.map((tag) => String(tag).trim())
.filter((tag) => tag.length > 0);
}
if (typeof rawTags === "string") {
return rawTags
.split(",")
.map((tag) => tag.trim())
.filter((tag) => tag.length > 0);
}
return [];
}
6. Configure the dashboard
The dashboard contains the map widget (with the menu attached) and an alarm table to verify created alarms.
{
"configuration": [
{
"type": "BoxLayout",
"id": "maps_create_alarm_layout",
"title": "",
"boxLayoutWidgetSettings": {
"orientation": "Vertical",
"components": [
{
"component": {
"id": "map_create_alarm",
"type": "Maps",
"title": "",
"menuReferences": [
"root.maps.menu"
],
"mapsWidgetSettings": {
"layerOptions": [
{
"layerId": "root.layers.points",
"activatedByDefault": true
}
],
"mapStyle": {
"dark": "mapbox://styles/mapbox/dark-v11",
"light": "mapbox://styles/mapbox/light-v11"
},
"centerPosition": [
7.153656,
46.80603
],
"zoomLevel": 9
}
},
"size": 0.55
},
{
"component": {
"type": "AlarmTable",
"id": "map_alarm_table",
"title": "Created alarms",
"menuReferences": [
"root.alarms.menus"
],
"alarmWidgetSettings": {
"defaultFilter": "root.alarms.filters.all",
"defaultView": "root.alarms.views.default"
}
},
"size": 0.45
}
]
}
}
],
"layout": {
"lg": [
{
"w": 12,
"h": 6,
"x": 0,
"y": 0,
"i": "maps_create_alarm_layout"
}
]
},
"breakpoints": {
"lg": 1200,
"md": 996,
"sm": 768,
"xs": 480,
"xxs": 0
},
"cols": {
"lg": 12,
"md": 10,
"sm": 6,
"xs": 4,
"xxs": 2
},
"rowHeight": 150
}
{
"moduleId": "modules.web.web-1",
"title": "Map create alarm",
"description": "Right click on map to open a form and create alarms through a script",
"tags": []
}
7. Validate the behavior
Open the
mapsdashboard.Click on the map and choose
Create alarm here.Edit form fields if needed, then click
Create.Confirm the success snackbar and verify a new alarm appears in
Created alarms.