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 UTC — set when the alarm clears
actualValue?: string | number;
expectedValue?: string | number;
breachedField?: string;
tolerancePercent?: number;
metadata?: Record<string, unknown>;
}| Field | Required | Description |
|---|---|---|
alarmId | Yes | Stable identifier for this alarm instance across raise/clear updates. |
severity | Yes | Priority classification — see severity levels below. |
code | Yes | Structured alarm type — see alarm codes below. |
message | Yes | Human-readable description of the condition. |
deviceId | No | Sub-device externalId when the alarm is scoped to a specific asset. Omitted for site-level alarms. |
raisedAt | Yes | ISO 8601 UTC timestamp when the condition was first detected. |
clearedAt | No | ISO 8601 UTC timestamp when the alarm cleared. If present, the condition has resolved. |
actualValue | No | Actual measured value for machine-readable alarms. |
expectedValue | No | Expected target or threshold value. |
breachedField | No | Constraint or field that was breached. |
tolerancePercent | No | Tolerance used when evaluating a sustained deviation. |
metadata | No | Arbitrary 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',
}| Value | Description |
|---|---|
P1_CRITICAL | Immediate operator response required. The site is unable to fulfil dispatch. |
P2_MAJOR | Degraded operation. The site is partially functional; response required within the operating window. |
P3_MINOR | No immediate operational impact. Schedule investigation. |
P4_INFO | Informational event. No action required. |
Alarm codes
Emitted today
| Code | Severity | When Voke raises it |
|---|---|---|
CONSTRAINT_VIOLATION | typically P2_MAJOR | Voke rejected a setpoint because it breached one of the site's configured constraints (max import/export, SOC bounds, ramp rate, etc.). |
OPERATING_MODE_NOT_HONORED | typically P2_MAJOR | Reported operating mode differs from the mode Voke last commanded, for a sustained duration. |
SCHEDULE_SLOT_MISSED | typically P2_MAJOR | Grid 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
messageIdas 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
ackafter processing and keep a short deduplication window (an in-memory set with TTL, or a bloom filter keyed onmessageId). alarmIdis stable across raise/clear updates for the same logical alarm instance — use it to correlateclearedAtupdates 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}`);
}Related pages
- VCP data model —
AlarmPayloadfield reference and full enum listings. - Per-org AMQP queues — queue layout and routing-key conventions.
- Commands —
CONSTRAINT_VIOLATIONalarms are triggered when Voke rejects acommand.site-setpointfor breaching configured limits.