This is a development version of the documentation. Content may change without notice.
Voke Documentation
Partner API

Alarms

Consume Voke-emitted operational alarms on the event.alarm queue.

Overview

Alarms are operational conditions that Voke's control plane detects on a site and forwards to your partner queue. They surface deviations Voke wants you to know about — a setpoint that breached site constraints, a sustained drift from a scheduled slot, an operating mode that didn't take effect — packaged in a standard payload so your ESM can react or log without parsing free-text.

Voke publishes each alarm to your org's vcp.{slug}.event.alarm queue with routing key {slug}.event.alarm. Delivery is one-way: there is no inbound alarm-ack route in VCP today.

What you'll actually receive today. Three alarm codes are emitted in the current release: CONSTRAINT_VIOLATION, OPERATING_MODE_NOT_HONORED, SCHEDULE_SLOT_MISSED. The other 13 codes in VcpAlarmCode are part of the long-term contract — your consumer should accept them, but Voke does not yet emit them. New emitters land per the VCP roadmap without breaking the wire format.


Alarm payload

interface AlarmPayload {
  alarmId: string;              // Unique alarm instance ID
  severity: VcpAlarmSeverity;   // Severity classification
  code: VcpAlarmCode;           // Structured alarm type
  message: string;              // Human-readable description
  deviceId?: string;            // Sub-device externalId (when alarm is device-scoped)
  raisedAt: string;             // ISO 8601 UTC — when the condition was detected
  clearedAt?: string;           // ISO 8601 UTCset when the alarm clears
  actualValue?: string | number;
  expectedValue?: string | number;
  breachedField?: string;
  tolerancePercent?: number;
  metadata?: Record<string, unknown>;
}
FieldRequiredDescription
alarmIdYesStable identifier for this alarm instance across raise/clear updates.
severityYesPriority classification — see severity levels below.
codeYesStructured alarm type — see alarm codes below.
messageYesHuman-readable description of the condition.
deviceIdNoSub-device externalId when the alarm is scoped to a specific asset. Omitted for site-level alarms.
raisedAtYesISO 8601 UTC timestamp when the condition was first detected.
clearedAtNoISO 8601 UTC timestamp when the alarm cleared. If present, the condition has resolved.
actualValueNoActual measured value for machine-readable alarms.
expectedValueNoExpected target or threshold value.
breachedFieldNoConstraint or field that was breached.
tolerancePercentNoTolerance used when evaluating a sustained deviation.
metadataNoArbitrary structured context (measurement values, threshold references, etc.).

Severity levels

enum VcpAlarmSeverity {
  P1_CRITICAL = 'P1_CRITICAL',
  P2_MAJOR    = 'P2_MAJOR',
  P3_MINOR    = 'P3_MINOR',
  P4_INFO     = 'P4_INFO',
}
ValueDescription
P1_CRITICALImmediate operator response required. The site is unable to fulfil dispatch.
P2_MAJORDegraded operation. The site is partially functional; response required within the operating window.
P3_MINORNo immediate operational impact. Schedule investigation.
P4_INFOInformational event. No action required.

Alarm codes

Emitted today

CodeSeverityWhen Voke raises it
CONSTRAINT_VIOLATIONtypically P2_MAJORVoke rejected a setpoint because it breached one of the site's configured constraints (max import/export, SOC bounds, ramp rate, etc.).
OPERATING_MODE_NOT_HONOREDtypically P2_MAJORReported operating mode differs from the mode Voke last commanded, for a sustained duration.
SCHEDULE_SLOT_MISSEDtypically P2_MAJORGrid power drifts outside the active schedule slot tolerance for a sustained duration.

Reserved (forward-compatible — your consumer should accept these, Voke does not emit them today)

enum VcpAlarmCode {
  COMM_LOSS                 = 'COMM_LOSS',
  COMM_DEGRADED             = 'COMM_DEGRADED',
  BESS_SOC_LOW              = 'BESS_SOC_LOW',
  BESS_SOC_HIGH             = 'BESS_SOC_HIGH',
  BESS_TEMP_HIGH            = 'BESS_TEMP_HIGH',
  BESS_FAULT                = 'BESS_FAULT',
  FVE_CURTAILED             = 'FVE_CURTAILED',
  FVE_INVERTER_FAULT        = 'FVE_INVERTER_FAULT',
  GRID_EXPORT_LIMIT_REACHED = 'GRID_EXPORT_LIMIT_REACHED',
  GRID_IMPORT_LIMIT_REACHED = 'GRID_IMPORT_LIMIT_REACHED',
  SETPOINT_DEVIATION        = 'SETPOINT_DEVIATION',
  SETPOINT_UNACHIEVABLE     = 'SETPOINT_UNACHIEVABLE',
  FALLBACK_ACTIVATED        = 'FALLBACK_ACTIVATED',
  OPERATING_MODE_NOT_HONORED = 'OPERATING_MODE_NOT_HONORED',
  SCHEDULE_SLOT_MISSED      = 'SCHEDULE_SLOT_MISSED',
  CONSTRAINT_VIOLATION      = 'CONSTRAINT_VIOLATION',
}

The full enum is part of the VCP v1.1 contract. New emitters are added without breaking the wire format — a consumer that handles only the three "emitted today" codes is correct today, but tomorrow's release may surface additional codes from the same enum.


Acknowledgement model

There is no VCP alarm-ack message today. Alarms delivered to partners are informational and the vcp.{slug}.event.alarm queue is a one-way outbound stream — do not publish anything back to it. If your ESM acknowledges alarms internally, keep that strictly local.

Alarm acknowledgement may be added in a future VCP version. Until then, treat event.alarm as one-way.


Deduplication

  • Use the outer VCP envelope's messageId as the primary deduplication key. Voke assigns a fresh UUIDv4 for every published alarm.
  • On consumer reconnect, RabbitMQ may redeliver messages that were unacknowledged before the channel dropped. Always ack after processing and keep a short deduplication window (an in-memory set with TTL, or a bloom filter keyed on messageId).
  • alarmId is stable across raise/clear updates for the same logical alarm instance — use it to correlate clearedAt updates with the original raise.

Example consumer

import { connectVoke, type VokeAmqpCreds } from './amqp-connect';

interface AlarmPayload {
  alarmId: string;
  severity: string;
  code: string;
  message: string;
  deviceId?: string;
  raisedAt: string;
  clearedAt?: string;
  metadata?: Record<string, unknown>;
}

async function startAlarmConsumer(creds: VokeAmqpCreds) {
  const { ch, outbound } = await connectVoke(creds);
  const seen = new Set<string>();

  await ch.consume(outbound.alarm, (msg) => {
    if (!msg) return;
    try {
      const envelope = JSON.parse(msg.content.toString()) as {
        messageId: string;
        siteId: string;
        payload: AlarmPayload;
      };

      if (seen.has(envelope.messageId)) {
        ch.ack(msg);
        return;
      }
      seen.add(envelope.messageId);

      const { payload } = envelope;
      const cleared = payload.clearedAt ? ` (cleared at ${payload.clearedAt})` : '';
      console.log(
        `[${envelope.siteId}] ALARM ${payload.severity} ${payload.code}` +
          ` — ${payload.message}${cleared}`,
      );

      ch.ack(msg);
    } catch {
      ch.nack(msg, false, false);
    }
  });

  console.log(`Consuming alarms from ${outbound.alarm}`);
}

  • VCP data modelAlarmPayload field reference and full enum listings.
  • Per-org AMQP queues — queue layout and routing-key conventions.
  • CommandsCONSTRAINT_VIOLATION alarms are triggered when Voke rejects a command.site-setpoint for breaching configured limits.

On this page