Skip to content
Stand with Ukraine flag

Alarm Widget

Alarm widgets subscribe to alarm data instead of telemetry. The platform delivers alarms matching the configured filter (severity, status, type) through the alarm subscription, and calls self.onDataUpdated() whenever the alarm list changes.

To activate the alarm subscription, call self.ctx.defaultSubscription.subscribeForAlarms(pageLink, null) inside onInit and assign self.ctx.defaultSubscription.alarmSource to the scope. This wires up the subscription so the platform populates self.ctx.defaultSubscription.alarms.data and triggers onDataUpdated whenever the alarm list changes.

Each alarm object delivered by the platform contains:

PropertyTypeDescription
id.idstringAlarm ID
typestringAlarm type (e.g., “High Temperature”)
severitystringCRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE
statusstringACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK
createdTimenumberTimestamp when the alarm was created (ms)
ackTsnumberTimestamp when acknowledged (0 if not yet)
clearTsnumberTimestamp when cleared (0 if not yet)
originatorNamestringDisplay name of the entity that raised the alarm

Use this type when the widget’s purpose is to monitor, display, or act on alarms:

  • Custom alarm tables with domain-specific columns and sorting
  • Severity-based dashboards showing counts or indicators per level
  • Operator consoles where alarms need acknowledgment or clearing
  • Compact alarm badges on overview dashboards

Example 1: Alarm table with acknowledge and clear

Section titled “Example 1: Alarm table with acknowledge and clear”

A scrollable table listing active alarms with severity-colored rows and per-row buttons to acknowledge or clear each alarm.

Alarm table widget showing one critical alarm with acknowledge and clear action buttons
  1. Open Widget Library → open your bundle → click + → select Alarm as the type.

  2. In the HTML tab, paste:

    <div class="alarm-table-container">
    <div class="alarm-header">
    <span class="alarm-count">{{alarms.length}} alarms</span>
    </div>
    @if (alarms.length === 0) {
    <div class="no-alarms">
    <mat-icon>check_circle</mat-icon>
    <span>No active alarms</span>
    </div>
    }
    <div class="alarm-list">
    <div *ngFor="let alarm of alarms"
    class="alarm-row"
    [ngClass]="'severity-' + alarm.severity?.toLowerCase()">
    <div class="alarm-info">
    <div class="alarm-type">{{alarm.type}}</div>
    <div class="alarm-meta">
    <span class="alarm-severity">{{alarm.severity}}</span>
    <span class="alarm-time">{{formatTime(alarm.createdTime)}}</span>
    <span class="alarm-originator">{{alarm.originatorName}}</span>
    </div>
    </div>
    <div class="alarm-actions">
    @if (!alarm.ackTs) {
    <button mat-icon-button matTooltip="Acknowledge" (click)="ackAlarm(alarm)">
    <mat-icon>done</mat-icon>
    </button>
    }
    @if (!alarm.clearTs) {
    <button mat-icon-button matTooltip="Clear" (click)="clearAlarm(alarm)">
    <mat-icon>clear</mat-icon>
    </button>
    }
    </div>
    </div>
    </div>
    </div>
  3. In the CSS tab, paste:

    .alarm-table-container {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    }
    .alarm-header {
    padding: 8px 12px;
    border-bottom: 1px solid #eaecf0;
    }
    .alarm-count {
    font-size: 14px;
    font-weight: 600;
    color: #344054;
    }
    .no-alarms {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 32px;
    color: #667085;
    }
    .no-alarms mat-icon { color: #12b76a; }
    .alarm-list {
    flex: 1;
    overflow-y: auto;
    }
    .alarm-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 12px;
    border-bottom: 1px solid #eaecf0;
    border-left: 4px solid transparent;
    }
    .alarm-row.severity-critical { border-left-color: #f04438; background: #fef3f2; }
    .alarm-row.severity-major { border-left-color: #f79009; background: #fffaeb; }
    .alarm-row.severity-minor { border-left-color: #eaaa08; background: #fefdf0; }
    .alarm-row.severity-warning { border-left-color: #2e90fa; background: #eff8ff; }
    .alarm-row.severity-indeterminate { border-left-color: #98a2b3; background: #f9fafb; }
    .alarm-info { flex: 1; min-width: 0; }
    .alarm-type {
    font-size: 14px;
    font-weight: 500;
    color: #344054;
    }
    .alarm-meta {
    display: flex;
    gap: 12px;
    margin-top: 4px;
    font-size: 12px;
    color: #667085;
    }
    .alarm-severity {
    font-weight: 600;
    text-transform: uppercase;
    }
    .alarm-actions { display: flex; gap: 4px; }
  4. In the JavaScript tab, paste:

    let alarmService;
    self.onInit = function() {
    let $injector = self.ctx.$scope.$injector;
    alarmService = $injector.get(
    self.ctx.servicesMap.get('alarmService'));
    let pageLink = self.ctx.pageLink(100);
    pageLink.typeList = self.ctx.widgetConfig.alarmTypeList;
    pageLink.statusList = self.ctx.widgetConfig.alarmStatusList;
    pageLink.severityList = self.ctx.widgetConfig.alarmSeverityList;
    pageLink.searchPropagatedAlarms = self.ctx.widgetConfig.searchPropagatedAlarms;
    self.ctx.defaultSubscription.subscribeForAlarms(pageLink, null);
    self.ctx.$scope.alarmSource = self.ctx.defaultSubscription.alarmSource;
    self.ctx.$scope.alarms = [];
    self.ctx.$scope.formatTime = function(ts) {
    return moment(ts).fromNow();
    };
    self.ctx.$scope.ackAlarm = function(alarm) {
    alarmService.ackAlarm(alarm.id.id).subscribe(function() {
    alarm.ackTs = Date.now();
    self.ctx.detectChanges();
    });
    };
    self.ctx.$scope.clearAlarm = function(alarm) {
    alarmService.clearAlarm(alarm.id.id).subscribe(function() {
    alarm.clearTs = Date.now();
    self.ctx.detectChanges();
    });
    };
    };
    self.onDataUpdated = function() {
    let subscription = self.ctx.defaultSubscription;
    if (subscription && subscription.alarms) {
    self.ctx.$scope.alarms = subscription.alarms.data;
    }
    self.ctx.detectChanges();
    };
    self.typeParameters = function() {
    return {
    hasDataPageLink: true,
    singleEntity: false,
    warnOnPageDataOverflow: false
    };
    };
    self.actionSources = function() {
    return {
    'rowClick': {
    name: 'widget-action.row-click',
    multiple: false
    }
    };
    };

    Alarm subscriptionsubscribeForAlarms(pageLink, null) activates the subscription using the widget’s alarm filter settings (type, status, severity, propagation). alarmSource exposes the bound entity for use in templates. Without this call, onDataUpdated will never fire and alarms.data will be undefined.

    Alarm data access — alarms arrive at self.ctx.defaultSubscription.alarms.data, a PageData<AlarmInfo> object where .data is the flat array of alarm objects.

    Alarm servicealarmService.ackAlarm(id) and alarmService.clearAlarm(id) both return Observables. After the operation completes, update the alarm’s timestamp directly on the object and call detectChanges() to re-render — no need to reload the full list.

    hasDataPageLink: true — enables server-side pagination for large alarm lists. singleEntity: false — allows alarms from multiple entities.

  5. In the Settings form tab, click Import from JSON → select the JSON content tab → paste:

    [
    {
    "id": "severityColors",
    "name": "Custom severity colors",
    "group": "Appearance",
    "type": "switch",
    "default": false,
    "required": false
    }
    ]
  6. Click Save → name it “Alarm Table”.

  7. Add the widget to a dashboard:

    • In the Alarm source configuration, select the entity (or entities) to monitor
    • Configure the alarm filter: severity levels, status, alarm type
    • The widget shows matching alarms with severity-colored rows
    • The Acknowledge button hides after an alarm is acknowledged; Clear hides after cleared

A compact overview widget showing the alarm count per severity level — useful as a status tile on overview dashboards where you want at-a-glance severity visibility without a full table.

Alarm severity counter widget showing counts for Critical, Major, Minor, Warning, and Indeterminate severity levels
  1. Open Widget Library → open your bundle → click + → select Alarm as the type.

  2. In the HTML tab, paste:

    <div class="severity-grid">
    <div class="severity-card"
    *ngFor="let item of rows"
    [ngClass]="'card-' + item.key">
    <span class="sev-count">{{item.count}}</span>
    <span class="sev-label">{{item.label}}</span>
    </div>
    </div>
  3. In the CSS tab, paste:

    .severity-grid {
    width: 100%;
    height: 100%;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
    gap: 8px;
    padding: 8px;
    box-sizing: border-box;
    align-content: center;
    }
    .severity-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 12px 8px;
    border-radius: 8px;
    gap: 4px;
    }
    .sev-count {
    font-size: 28px;
    font-weight: 700;
    line-height: 1;
    }
    .sev-label {
    font-size: 11px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    text-align: center;
    }
    .card-critical { background: #fef3f2; color: #f04438; }
    .card-major { background: #fffaeb; color: #f79009; }
    .card-minor { background: #fefdf0; color: #eaaa08; }
    .card-warning { background: #eff8ff; color: #2e90fa; }
    .card-indeterminate { background: #f9fafb; color: #98a2b3; }
  4. In the JavaScript tab, paste:

    const SEVERITY_ROWS = [
    { key: 'critical', label: 'Critical', severity: 'CRITICAL' },
    { key: 'major', label: 'Major', severity: 'MAJOR' },
    { key: 'minor', label: 'Minor', severity: 'MINOR' },
    { key: 'warning', label: 'Warning', severity: 'WARNING' },
    { key: 'indeterminate', label: 'Indeterminate', severity: 'INDETERMINATE' }
    ];
    self.onInit = function() {
    let pageLink = self.ctx.pageLink(100);
    pageLink.typeList = self.ctx.widgetConfig.alarmTypeList;
    pageLink.statusList = self.ctx.widgetConfig.alarmStatusList;
    pageLink.severityList = self.ctx.widgetConfig.alarmSeverityList;
    pageLink.searchPropagatedAlarms = self.ctx.widgetConfig.searchPropagatedAlarms;
    self.ctx.defaultSubscription.subscribeForAlarms(pageLink, null);
    self.ctx.$scope.alarmSource = self.ctx.defaultSubscription.alarmSource;
    self.ctx.$scope.rows = SEVERITY_ROWS.map(function(r) {
    return { ...r, count: 0 };
    });
    };
    self.onDataUpdated = function() {
    let subscription = self.ctx.defaultSubscription;
    let alarms = (subscription && subscription.alarms)
    ? subscription.alarms.data : [];
    let counts = {};
    SEVERITY_ROWS.forEach(function(r) { counts[r.severity] = 0; });
    alarms.forEach(function(a) {
    if (counts[a.severity] !== undefined) counts[a.severity]++;
    });
    self.ctx.$scope.rows = SEVERITY_ROWS.map(function(r) {
    return { ...r, count: counts[r.severity] };
    });
    self.ctx.detectChanges();
    };
    self.typeParameters = function() {
    return {
    hasDataPageLink: true,
    singleEntity: false,
    warnOnPageDataOverflow: false
    };
    };

    The widget counts alarms by severity using a simple counts accumulator. SEVERITY_ROWS defines the display order and maps severity strings to CSS class keys. On each onDataUpdated, the counts are recalculated from the full alarm list and pushed to $scope.rows.

  5. No settings form needed for this widget. Click Save → name it “Alarm Severity Counter”.

  6. Add to a dashboard → configure the alarm source and filter. Each severity tile shows its count; tiles with zero alarms still render with 0 so the layout stays consistent.

StatusAcknowledgedCleared
ACTIVE_UNACKNoNo
ACTIVE_ACKYesNo
CLEARED_UNACKNoYes
CLEARED_ACKYesYes