OpenADR
The OpenADR page is GEM's demand-response console. It configures GEM as a Virtual End Node (VEN) on one or more utility demand-response programs, shows what those programs are asking for, curtails load to meet the ask, and keeps a permanent record of what was done about every event.
Open OpenADR
OpenADR is a premium integration. Adding an OpenADR device — and using this page — requires the OpenADR product in your license. Existing devices keep running if a license later changes. See License.
Concepts
| Term | Meaning |
|---|---|
| VTN | Virtual Top Node — the utility's demand-response server. |
| VEN | Virtual End Node — this GEM controller, as the program sees it. One VEN per program enrolment. |
| Event | A request from the VTN covering a time window. It carries one or more signals. |
| Signal | What the program is asking for, and in what units — a shed level, a price, a percentage, or a kW reduction. |
| Directive | GEM's normalized reading of an event: what the site should do right now, regardless of which signal kind produced it. |
| Resource | A curtailable load, with the kW it is worth and the rules governing when it may be shed. |
Protocols
GEM implements both OpenADR profiles as first-class drivers.
OpenADR 3.0 (openadr3) | OpenADR 2.0b (openadr2) | |
|---|---|---|
| Transport | REST / JSON | XML over HTTP |
| Auth | OAuth2 client credentials (mTLS also supported) | Mutual TLS, or basic auth to a gateway |
| Delivery | Polling, or webhook push | Polling |
| Use it for | New program enrolments | Existing 2.0b programs, or an on-prem eisy/ISY gateway |
Both drivers share one engine, so event scheduling, signal interpretation, curtailment, staggered restore, history and restart survival behave identically. Moving a site from a 2.0b program to a 3.0 program means pointing a different driver at the same curtailable resources — the response configuration does not change.
XML signatures are not implemented in the 2.0b driver. Programs that mandate signed payloads need a certified gateway in front.
Signals — what actually drives load
An event's numbers only mean something in the context of their signal kind. A level of 2 means "high"; a price of 2 means $2/kWh; a setpoint of 2 might mean two degrees of offset. GEM classifies every signal and refuses to act on one it cannot classify.
| Signal kind | Example source | What GEM does |
|---|---|---|
| Level | 2.0b simple, 3.0 SIMPLE | Sheds resources at or below that level; runs the matching level macro. |
| kW target | LOAD_DISPATCH, IMPORT_CAPACITY_LIMIT | Selects resources in rank order until the target is met, then stops. |
| Percentage | LOAD_CONTROL | Converts to kW against live site demand, then as above. |
| Price | ELECTRICITY_PRICE, 3.0 PRICE | Recorded. Drives load only if price thresholds are configured. |
| Setpoint / delta | CONTROL_SETPOINT | Recorded and exposed; never inferred to be a shed level. |
| Unrecognized | anything else | Recorded, shown in the event detail, never acted on. |
When an event carries several signals, the most direct instruction wins: a kW dispatch beats a level, and a level beats a price the site has to interpret.
Level 0 means normal operations. The event is tracked, attributes update, and the audit trail records it — but nothing sheds.
Price thresholds
A price is information, not an instruction, so GEM never guesses that a dollar figure implies a shed level. Configure a threshold table on the Connection tab and a real-time-price program drives exactly the same response as a level program:
[
{"price": 0.25, "level": 1},
{"price": 0.50, "level": 2},
{"price": 1.00, "level": 3}
]
Prices below the lowest threshold produce no directive.
Choosing a VEN
One VEN is selected at a time and every tab works against it, so the tabs never disagree about what is running. The header carries the picker, the profile badge, whether the connection is up, and the current directive in one pill — visible from every tab, including Monitor. The device name beside them opens that device record in place.
Add VEN creates another enrolment; a site on two programs gets one VEN per program. With none configured yet the page offers both profiles directly, and defaults to 3.0.
Simulate event and Refresh are also in the header — see Simulating an event.
Overview tab
The state of the VEN, and whether it is ready for the next event.
- Demand response — the current directive in plain language, or
Idle/Opted out. - Curtailed now — how much load is being held off, and by how many resources.
- Readiness — whether a shed would actually work. "No resources", "no capacity available" and "42 kW curtailable across 9 resources" are all answers you want before an event, not during one.
- Active events and Scheduled — what is running now, with a progress bar, and what is coming.
Actions: Poll now, Re-register, and Opt in / Opt out.
While a VEN is opted out it still receives events, acknowledges them to the VTN, updates zone attributes and records the full decision trail — it simply does not run macros or curtail load. The opt state survives restarts, and in 2.0b the new opt type is pushed to the VTN immediately for every in-flight event.
Events tab
Timeline
Every event GEM knows about, past and future, on one axis with a now line. Bar colour is severity; pending events are outlined, completed ones faded. Click any bar to open it.
Event detail
The full record of one event: the window, the randomization GEM applied, the opt state sent, why it ended — and every signal as the VTN sent it, including the ones GEM deliberately did not act on, with its interval timeline.
What GEM did
The audit trail. Every decision, in order, including the decisions to do nothing:
14:58 Received none scheduled for 15:00 (randomized +47s)
15:00 Started none event window opened
15:00 Shed command_off curtail Rooftop HVAC (~25 kW)
15:00 Shed command_off curtail Pool Pump (~2 kW)
15:00 Shed none short of target by 13.00 kW — no further curtailable resource was available
17:00 Ended none event ended: completed
17:00 Restore command_on restore Pool Pump — event ended: completed
17:02 Restore command_on restore Rooftop HVAC — event ended: completed
An event GEM correctly ignored and an event GEM missed look identical afterwards unless the reason was written down, so refusals are recorded as first-class entries — "VEN is opted out", "macro 12 not found", "still inside its lockout window".
A row that ran a macro names it, and the name opens that macro in place — a settlement dispute is read with one event selected and its trail loaded, so checking what run_macro actually ran should not cost that. A row naming a macro id with no name is the "not found" entry itself, and stays plain text because there is nothing to open.
History
Completed and cancelled events, retained after the event ends. This is the record a settlement dispute asks for.
Response tab
How the site actually sheds.
Curtailable resources
A resource is one load GEM can curtail, and what it is worth:
| Field | Purpose |
|---|---|
| Label / Name | Identifies the resource in the console and the audit trail. |
| Category | HVAC, lighting, EV charging, pool, and so on. |
| Nominal reduction | kW this sheds. The planner sums these against the target. |
| Shed rank | Ascending curtailment order — the lowest number is shed first. |
| Shed from level | Lowest event level at which this resource may be shed. Applies to level events; a kW target uses rank instead. |
| Shed / restore action | A zone command, a macro, or a raw command payload. |
| Maximum curtailment | Released after this long, even if the event continues. |
| Lockout | Minimum rest between curtailments, so a compressor is not short-cycled. |
| Restore delay | Pins this resource's place in the staggered restore. |
Shed and restore actions use the same payload shape as per-zone action overrides, so anything expressible as a GEM command or macro is expressible as a curtailment:
{"command": "set_level", "zone_id": 12, "args": {"level": 30}}
{"macro_id": 42}
In the resource table, the Controls column names the zone, device or subsystem the resource drives, and a shed action that runs a macro names the macro. Both open in place, so confirming what a resource actually touches does not cost the unsaved macro mapping or the plan preview just run.
How a shed is planned
- With a kW or percentage target — resources are selected in rank order until the target is met, then selection stops. Everything below the line is reported as held in reserve. If the resources cannot reach the target, the shortfall is recorded on the event rather than silently ignored.
- With a plain level — every eligible resource at or below that level is shed, since a level event asks for severity rather than a number.
- Load already curtailed counts toward the target before anything new is chosen.
- Restores run in reverse shed order and are spread across the restore window, because returning every load in the same second is a demand spike that some programs penalize more heavily than the shed earned.
Plan preview
Ask "if the VTN wanted 40 kW, what would happen?" and get the exact selection, in order, with the shortfall spelled out — at commissioning, rather than during the first event of the summer.
Macro mapping
Macros remain fully supported and are the right tool for site-wide response that is not a curtailable load: notifications, site modes, occupant messaging.
| Response mode | Behavior |
|---|---|
| Auto (default) | Resources when any are defined, macros otherwise. |
| Resources | Resource plan only. |
| Macro | Level-mapped macros only. |
| Both | Resource plan and macros. |
Macro context variables
Shed / level macros:
| Variable | Description |
|---|---|
event_id | The VTN event identifier |
signal_level | Level currently in force |
directive_kind | level, target_kw, target_pct, price, setpoint |
target_kw | kW reduction requested, when the program expressed one |
price | Price in force, when the event carries a price signal |
start_time / end_time | ISO 8601 event window |
Restore macro: event_id, and reason — one of completed, cancelled, expired, withdrawn.
Telemetry tab
Most paying programs do not settle on the shed — they settle on metered evidence that the shed happened, at 5 to 15 minute granularity. A VEN can respond perfectly to every event and still earn nothing without a reporting feed, which is why this tab leads with the two ways it silently fails: nothing being measured, and nothing being sent.
Telemetry is opt-in. Enable it on the Connection tab for a VEN enrolled in a reporting program; leave it off and nothing is sampled.
What is measured
| Stream | Kind | Source |
|---|---|---|
| Site real power | averaged | Summed across every power-reporting device, via the same metric roles the energy dashboard uses. Any tagged driver contributes with no OpenADR-specific wiring. |
| Site energy | differenced | Cumulative energy counters. Reported as energy used during the interval, not the lifetime total. |
| Operating state | state | 0 normal, 1 curtailing. |
| Per-resource power | averaged | One stream per curtailable resource that declares a measured attribute. |
A resource with only a nominal kW figure is curtailed but not reported. A nameplate number is not a measurement, and reporting one as if it were is exactly what loses a settlement dispute. Give a resource a measured attribute to have it evidenced.
How intervals are built
GEM samples every 30 seconds by default and aggregates on demand, so two programs asking for different granularities are served from the same readings. Buckets align to the clock, so a 15-minute granularity lands on the quarter hours a utility expects.
The interval currently in progress is never sent — a partial interval reported as complete understates demand, and a VTN cannot tell the difference.
What the VTN asked for
The subscriptions table shows each active report request: which streams, the interval length, the send cadence, when it last succeeded and when it goes again.
- 2.0b — the VTN subscribes with
oadrCreateReportafter registration and names its own granularity and report-back duration. If this table is empty, the VTN has not asked; check the Monitor tab to confirm the metadata offer was accepted. - 3.0 — an event carries
reportDescriptorsthat create a reporting obligation for that event automatically. For data between events, set a standing report interval on the Connection tab.
A failed send is retried rather than skipped past: the window is only marked complete once the VTN accepts it, because a settlement gap costs more than a duplicate the VTN can de-duplicate by interval.
Send due reports flushes anything pending immediately, for commissioning.
Event performance
When telemetry is on, GEM measures what each event actually delivered and writes it to the event record.
- Baseline is captured the moment an event starts, before anything sheds — the average site demand over the hour ending 15 minutes before the start. The gap matters: a site that pre-cools before a known event would otherwise bake its own preparation into the baseline and under-report the reduction it delivered.
- Delivered is baseline minus the average demand across the event window.
- Performance is delivered against the requested kW, when the program expressed one.
All three appear on the event's audit trail. Where there is not enough telemetry, the event records "not measured" rather than a zero — a missing baseline is a known unknown, while a zero one would make every event look like a 100% over-delivery.
Only the pre-event window baseline is implemented. Day-matching methods (CAISO 10-in-10, PJM, ISO-NE) need days of historical load shape and are not yet available. A figure from one methodology should never be presented as if it came from another, so the method is recorded alongside the number.
Connection tab
Profile-specific settings, plus certificate health and response behavior.
OpenADR 3.0
| Field | Description |
|---|---|
| VTN base URL | The 3.0 API root including the version path, e.g. https://vtn.example.com/openadr3/3.0.0. |
| VEN client name | Must match the clientName the program registered for this site. |
| Program IDs | Comma-separated. Empty follows every program the VTN offers. |
| OAuth2 client ID / secret | Issued by the program. The secret is stored encrypted. |
| Token URL | Defaults to <base URL>/auth/token. |
| OAuth2 scope | Only if the program requires one. |
| Webhook push | Opt-in. Requires the VTN to reach this controller; polling continues as a safety net either way. |
OpenADR 2.0b
| Field | Description |
|---|---|
| Mode | Direct VEN to a utility VTN, or Gateway for an eisy/ISY device. |
| VTN URL | Base URL; GEM appends the /OpenADR2/Simple/2.0b/ service paths. |
| VEN name | Identifies this VEN during registration. |
| Gateway URL / username / password | Gateway mode only. The password is stored encrypted. |
TLS certificates
Paste PEM material for mutual TLS. GEM reads the certificate and reports its subject, issuer and remaining validity.
An expired client certificate is the most common way a VEN goes quietly dark — the VTN stops accepting the handshake, GEM logs a TLS error, and the site finds out from a settlement statement. Utility re-issuance can take weeks. The console warns at 30 days; act on it.
Enabling Allow insecure TLS disables certificate verification entirely, which means anything on the network path can impersonate the VTN and dispatch load-shed events at the building. Lab VTNs only.
Telemetry
| Field | Description |
|---|---|
| Enabled | Opt in to measuring and reporting. Off by default. |
| Sample interval | How often readings are taken. Reported intervals are averaged from these. |
| Standing report interval (3.0) | Report on this cadence even with no event running. Empty reports only when an event asks. |
| Standing report granularity (3.0) | Interval length for standing reports. Programs typically settle on 5 or 15 minutes. |
In 2.0b the cadence is not configurable here — the VTN names its own granularity and report-back duration when it subscribes.
Poll interval
Seconds between polls, floored at 5 seconds. In 2.0b a VTN-requested poll frequency (oadrRequestedOadrPollFreq) overrides the configured value; the effective interval is shown under Registration.
Monitor tab
The raw protocol exchange with the VTN — what GEM sent, what came back, status codes and errors, newest first, with a Failures only filter and a copy button.
This is the tab for commissioning against a real utility VTN. When a payload is rejected, the rejection and the exact body that caused it are both here, so nobody has to SSH into a live automation controller to read logs.
Simulating an event
The Simulate event button injects a synthetic event into the running VEN. It is deliberately not a "run the shed macro" shortcut — the event goes through the whole path: scheduling, signal interpretation, resource planning, curtailment, zone attributes, history, and the staggered restore at the end.
Choose the signal kind (level, kW, percentage or price), the value, when it starts, and how long it runs. Ramped steps the value across three intervals to exercise interval boundaries, where a multi-step program's response is most likely to be wrong.
Simulated events are flagged simulated in the timeline and history, and Cancel running simulations ends them and runs the restore.
Real loads are curtailed. On an occupied building, run a simulation outside occupied hours — or opt out first, which records the full decision trail without touching anything.
Zone attributes
Assign zones to the VEN device (no address required) and GEM maintains three read-only attributes on each. They update even while the VEN is opted out:
| Attribute | Type | Description |
|---|---|---|
dr_event_active | bool | 1 while a DR event is active |
dr_signal_level | int | Level currently in force |
dr_event_id | string | Current event ID, empty when idle |
Use these in Triggers to drive automation that is not modelled as a curtailable resource.
How it works
- Registration — 3.0 acquires an OAuth2 token and finds or creates its VEN record; 2.0b performs the
EiRegisterPartyexchange and persists the VEN and registration IDs. A failure at boot is retried on every poll. - Event delivery — polling by default; 3.0 can also receive webhook push. Both profiles distribute the complete live event set, so an event that disappears has been withdrawn. A pushed event is applied without withdrawing anything, because a single push proves nothing about the others.
- Scheduling — events are tracked as pending until their start time. A day-ahead notification sheds nothing on the day it arrives. When the VTN allows a randomization window, GEM picks a random offset inside it so every VEN on the feeder does not drop load in the same instant.
- Response — at start, GEM derives the directive and either plans a resource shed or runs the level macro. Interval boundaries, event modifications and overlapping events all re-derive the whole desired state and move to it, so a level stepping down releases resources rather than leaving them held.
- Ending — an event ends when the VTN cancels or completes it, when it drops out of distribution, or when its scheduled end passes. When the last active event ends, curtailed resources are restored on a staggered ramp and the restore macro runs. Overlapping events do not restore each other.
- Recovery — after 5 consecutive failed polls the device shows Disconnected; 2.0b re-registers after 10, and on VTN request or registration cancellation.
- Restart survival — open events are reloaded from the database and curtailment state from the device record, so a controller that restarts mid-event resumes tracking and still restores.
Related documentation
- Macros — building event response macros
- Triggers — attribute-based automation
- Zones — action override payload shape
- Power Management — the metric roles that supply live site demand
- Dashboard — active events appear on the Energy & Demand widget