OPC UA
Driver: opc_ua
OPC UA (Unified Architecture, IEC 62541) client. Reads, writes, browses, and subscribes to nodes on industrial controllers, building-management systems, and any vendor speaking opc.tcp. Supports anonymous and username/password auth, and the three OPC UA security modes (None / Sign / SignAndEncrypt). Built on node-opcua.
Open OPC UA Explorer
Once an opc_ua device is configured, the explorer can browse the server's address space, read and write node values, subscribe to value-change events, and run endpoint discovery. Unlike BACnet, KNX, and Modbus, OPC UA is a core integration — it does not require a separate license product to add a device or open the explorer.
Prerequisites
- An OPC UA server reachable from the GEM host via
opc.tcp. - For non-
Nonesecurity modes: the server's application certificate must be trusted by GEM (or auto-accepted viaauto_trust_server_certon initial connect). - For username/password auth: credentials provisioned on the server.
Setup
- From Open Devices, add a device and pick driver type opc_ua, then set its
endpoint_urlattribute toopc.tcp://host:port[/path]. The conventional OPC UA discovery port is 4840, but always use the host and port the server documents — some servers return session endpoints on a different port, which the Endpoints tab will reveal. - Optionally set
security_mode+security_policyand credentials. Many servers expose an unsecuredNone / Noneendpoint AND secured endpoints on the same URL — pick the pair the server advertises (use the Endpoints tab to enumerate). Reload the device after changing any connection, security, or credential attribute. - Open Open OPC UA Explorer, pick your server from the OPC UA Server selector at the top, then browse the address space from the Objects folder and identify the NodeIds you care about.
- To bind a NodeId to a GEM zone, use the explorer's Map to Zone tab — or set the zone attribute
opc_ua_nodeto the NodeId (e.g.ns=2;s=Temperature) andopc_ua_data_typeto the OPC UA DataType name (Boolean,Int32,Double,String, …) by hand. The data type is required for writes so stringified values coerce correctly. Mapped nodes are auto-monitored so value changes reflect onto the zone — see Binding nodes to zones below.
Attribute reference
Device
| Attribute | Required | Default | Description |
|---|---|---|---|
endpoint_url | yes | — | opc.tcp://host:port[/path] |
security_mode | no | None | None, Sign, or SignAndEncrypt. Must match an endpoint the server advertises. |
security_policy | no | None | None, Basic256Sha256, Aes128_Sha256_RsaOaep, or Aes256_Sha256_RsaPss. |
username | no | — | Leave blank for anonymous auth. |
password | no | — | Required when username is set. Stored encrypted via the secure-attribute pipeline. |
auto_trust_server_cert | no | false | When true, the server's certificate is auto-added to the trusted store on first connect. Lab / dev only — production should pre-trust certs explicitly. |
session_timeout_ms | no | 60000 | How long the server may keep the session alive without activity. |
requested_publishing_interval_ms | no | 1000 | How often the server publishes monitored-item updates for our subscription. |
op_failure_threshold | no | 5 | Consecutive op failures before the watchdog considers recycling the client. |
op_staleness_ms | no | 120000 | How long without a successful op before the threshold can trip a recycle. |
Zone
| Attribute | Required | Default | Description |
|---|---|---|---|
opc_ua_node | no | — | NodeId in standard form, e.g. ns=2;s=MyVariable or ns=0;i=2253. A read_value / write_value / subscribe_node command aimed at this zone (with no explicit node_id) targets this node — see Binding nodes to zones below. |
opc_ua_data_type | no | — | OPC UA DataType name (Boolean, Int16, UInt32, Float, Double, String, …). Required for writes so stringified values coerce correctly; reads infer from the server response. Used as the fallback data_type when a write_value aimed at this zone omits it. |
Binding nodes to zones
The easiest path is the Map to Zone tab in the OPC UA Explorer: select a node in the Explorer tab, switch to Map to Zone, pick (or inline-create) a subsystem/zone, optionally choose the write DataType, Test Read to confirm the live value, and Map Node. The mapper warns when the chosen zone isn't bound to the OPC UA device (with a one-click bind), writes the opc_ua_node + opc_ua_data_type attributes, and immediately subscribes the node.
A mapped zone gets two behaviors:
- Zone-scoped commands —
read_value,write_value,subscribe_node, orunsubscribe_nodeaimed at the zone (from a macro, trigger, or the command surface) without an explicitnode_idtarget the zone'sopc_ua_node. Forwrite_value, an omitteddata_typefalls back to the zone'sopc_ua_data_type. - Live value reflection — the driver monitors every bound zone's mapped node (auto-subscribed on connect, and immediately when mapped from the UI). Change notifications are written to the zone's
valueattribute, so triggers, history, and widgets can consume the live reading.
The driver still does not poll nodes on a timer — reflection is change-of-value via the server's subscription mechanism. For servers that don't support subscriptions, schedule a macro that runs read_value instead.
Bulk point mapping with the AI Assistant
Map to Zone binds one node at a time, which is the right tool for a handful of points. A Kepware or Ignition tag export, a browse listing pasted out of a vendor's docs, or a PLC with sixty tags is a different shape of job — and OPC UA is unforgiving here because every mistake is invisible in the admin UI:
- A malformed NodeId is never rejected by GEM. The server answers
BadNodeIdInvalidinto a console warning nobody reads. - A zone with an
opc_ua_nodebut noopc_ua_data_typereads perfectly, so it looks correctly wired — while every write throwswrite_value requires data_type, because the driver refuses to guess a type. - Two zones bound to one node are both written on every value change.
For that, the AI Assistant has a map_opc_ua_points skill — the OPC UA sibling of map_bacnet_points, map_modbus_points, map_mqtt_topics and map_knx_group_addresses. Paste the browse output and ask it for zones:
Map the boiler tags on the plant server —
ns=2;s=Boiler1.SupplyTemp,.ReturnTempand.Enable.
It creates (or updates) one zone per node bound to the OPC UA device, writing opc_ua_node and opc_ua_data_type. What it handles for you:
- NodeId grammar. Every id is parsed against the standard textual form — an optional
ns=<n>;prefix followed by exactly one ofi=,s=,g=orb=, with a string identifier allowed to contain;and=because it runs to the end of the string. A bare integer is rejected with the typed form to use, andi=85andns=0;i=85count as one node when checking for duplicates, because they are one node to the server. - DataType names, checked and canonicalized. Names are validated against the same table the driver's resolver uses and stored in canonical spelling, so
doublelands asDouble. A type outside the driver's explicit coercion branches is reported as pass-through — fine for reads, but a stringified write reaches the library raw. - Actuation. The driver implements no standard zone verb — its command set is browse / read / write / subscribe / call_method, with no
on,offorset_level— so a freshly-mapped zone streams values in but responds to nothing from a panel button. Mark an entry writable and the skill wires the zone'son_action/off_actionoverrides towrite_valueagainst that node with typed on/off values (true/falsefor Boolean,1/0for the numeric types, stated explicitly for String), so an output point actually actuates. Writable without a data type is a hard error rather than a write that fails later. - Validation before any write. The parent device must be on the
opc_uadriver chain, and no two zones may share a node — in the batch or against existing zones on that server. A single bad entry cancels the whole call, and a mid-batch failure deletes every zone it created and restores every attribute it touched.
Reloading is two-tier, and the second tier is the point: zones are reloaded individually, and then the device is reloaded once. Zone-mapped nodes are subscribed on the connect path only, so skipping the device reload leaves a newly-mapped node unmonitored and its zone empty indefinitely.
Ask for a dry run first whenever the assistant had to interpret a pasted listing — it returns the resolved node, canonical data type and write plan per zone and writes nothing. The cap is 100 zones per call. Every mapped node reflects onto the zone's value attribute, which is what triggers and history should read.
Use Map to Zone for fixing one node on one zone, and the Explorer's address-space tree to find what the server exposes — the assistant maps what discovery found, it does not discover.
Commands
| Command | Args | Notes |
|---|---|---|
get_endpoints | — | Discover what security configurations the server advertises. Useful when connect fails with BadSecurityModeRejected. |
browse_root | — | Browse from the OPC UA Objects folder (the standard entry point). |
browse_node | node_id, direction | Browse a specific node's references. Direction is Forward (default), Inverse, or Both. |
read_value | node_id | Read the present-value attribute of a node. |
read_values | node_ids | Batch read several nodes in a single request. Array or CSV. |
write_value | node_id, value, data_type | Write to a writable node. data_type is required for correct coercion. |
subscribe_node | node_id, sampling_interval | Set up a change-of-value monitored item. |
unsubscribe_node | node_id | Drop a previously-subscribed monitored item. |
call_method | object_id, method_id, input_arguments | Invoke an OPC UA Method node. |
get_diagnostics | — | Snapshot of client health: connection state, watchdog counters, active subscriptions, last client error. |
OPC UA Explorer (/admin/opc_ua)
Live operator surface for the configured opc_ua device. Pick the device from the OPC UA Server selector at the top of the page — the toolbar then shows its endpoint and configured security mode/policy, and Refresh devices re-reads the device list. The page has five tabs:
Add device in the toolbar opens the device editor in a modal so a new opc_ua server can be commissioned without leaving the explorer; Edit device opens the selected server the same way. Saving reloads the device list and re-selects the saved device, keeping the browsed node and current tab intact. When no OPC UA device exists yet, the empty-state Add a device link opens the same modal.
- Explorer — split-pane view. The left panel is a tree of the server's address space starting at the Objects folder; click any node to load its details on the right (Reload re-browses the root). The detail panel shows the current value — auto-read on selection, with a Read button to re-read — plus its DataType and status code, for any node class. Variable nodes additionally get a Write form (pick the DataType, type the value) and a Subscribe / Unsubscribe control with a configurable sampling interval (100 ms minimum).
- Map to Zone — bind the node selected in the Explorer to a GEM zone (see Binding nodes to zones above): pick or inline-create a subsystem/zone, optionally choose the write DataType, Test Read to confirm the live value, and Map Node. Existing mappings are listed below the form with a per-row remove.
- Endpoints — the Run GetEndpoints button queries the server and lists every endpoint URL, security mode, security policy, and accepted user-token policy. Use this when
connectfails withBadSecurityModeRejected— it shows exactly what the server offers. - Diagnostics — session up/down, watchdog counters (consecutive failures, client recycles, last successful op, last client error), active monitored items with their last cached value, and the configured endpoint / security settings. Refresh reloads on demand; Auto-refresh polls every 5 seconds.
- Settings — read-only summary of the device's current attribute values. Edit this device opens the device editor in a modal without leaving the explorer, and the device name links through to the Devices page; most changes need a device reload to take effect.
Value Coercion
OPC UA is strongly typed on the wire (every value carries a DataType tag), so the driver coerces stringified arguments at the boundary. A saved macro that passes value='false' with data_type='Boolean' produces false on the wire — not the JS-truthy string. Garbage values throw an explicit error and do not send the write.
write_value requires explicit data_type (refuses to guess from the JS type) — without it the underlying library's type inference is unreliable for stringified arguments.
Known limitations
- No certificate management UI in v1. Trusted/rejected certs live on disk under the GEM PKI directory.
auto_trust_server_cert=trueis the easy path for lab/dev; production deployments should pre-trust the server cert out of band. - One shared subscription per device. Every monitored item lives on a single shared OPC UA subscription. Matches the recommended pattern and is more efficient on the server, but means all monitored items share one publishing interval (
requested_publishing_interval_ms). - Manual subscriptions are in-memory and not restored on reload. Monitored items created with
subscribe_node(or the explorer's Subscribe button) live only for the current session. If the device reloads, or the connection drops and reconnects, the subscription list starts empty — re-issuesubscribe_node(for example from a macro that runs on connect) if you need it to persist. The exception is zone-mapped nodes (opc_ua_node): those are re-subscribed automatically on every connect. - Methods addressable by NodeId only.
call_methodrequires both an object NodeId and a method NodeId — browse-name-only addressing is not supported. Input arguments are passed to the server as supplied (an array of already-typed values); they are not coerced from strings the waywrite_valuecoerces its value.
Troubleshooting
| Symptom | Check |
|---|---|
connect fails with BadSecurityModeRejected | security_mode / security_policy don't match any endpoint the server advertises. Run get_endpoints (or open the Endpoints tab) and pick a matching pair. |
connect fails with BadCertificateUntrusted | Either set auto_trust_server_cert=true (dev/lab only) or copy the server cert into GEM's PKI trusted/certs directory. |
Read returns null with status BadNodeIdUnknown | The NodeId does not exist on this server. Browse from the root to find the correct identifier — namespace indexes are server-specific. |
Write returns BadTypeMismatch / BadValueOutOfRange | Specify data_type explicitly on the write — the driver coerces stringified args to the named OPC UA type. Without it the library tries to infer and often gets it wrong for numeric strings. |
| Subscription "stops working" | Check the active monitored items in the Diagnostics tab. Sampling intervals well below the server's publishingInterval won't produce more frequent updates. Remember monitored items reset on a device reload — re-issue subscribe_node if the list is empty. |
A zone bound with opc_ua_node never shows a value | The mapped node is monitored via the server's subscription mechanism (auto-subscribed on connect) and the zone's value attribute only updates on change — run read_value once, or wait for the value to change on the server. If nothing ever arrives, check the Diagnostics tab for the monitored item; servers that don't support subscriptions need a macro-driven read_value instead. If the mapping was added by hand-editing attributes, reload the zone so the driver picks it up. |
Related Documentation
- OPC UA driver — Device attribute and command reference
- Devices — Creating the OPC UA device
- Zones — Binding NodeIds via
opc_ua_node - Attributes — Zone attribute mapping