Read and write OPC UA values
Read and write the values of OPC UA nodes, read node attributes, and call OPC UA methods through operations that the OPC UA gateway executes.
This page is the agent skill cumulocity-read-write-opcua-values. Save it as cumulocity-read-write-opcua-values/SKILL.md in the skills folder of your agent: .agents/skills/ for Codex, GitHub Copilot, Cursor, and Gemini CLI, or .claude/skills/ for Claude Code.
It builds on cumulocity-authentication: install that skill too.
API reference
Core API-
GET
/Retrieve a specific managed objectinventory/ managedObjects/ {id} -
POST
/Create an operationdevicecontrol/ operations -
GET
/Retrieve a specific operationdevicecontrol/ operations/ {id}
Linked from the documentation
- Operations in OPC UA
To read or write a value on an OPC UA server, you create an operation in Cumulocity. The OPC UA gateway picks up the operation, executes it on the OPC UA server, and writes the result back into the same operation. See Operations for all OPC UA operations.
How the gateway executes OPC UA operations
OPC UA operations follow the usual operation statuses, see Send an operation to a device. In addition, the following rules apply:
- The
deviceIdof the operation is the OPC UA server or a device that a device protocol generated below the server. On a generated device, you can only run operations for nodes, not general operations such as an address space scan. - Each operation contains exactly one
c8y_ua_command_*fragment. Do not put adeviceIdor an operation ID into the fragment. - The gateway does not list the
c8y_ua_command_*operations inc8y_SupportedOperations. Do not check this fragment before you create an operation. - The gateway writes the result as a string into the property
c8y_Command.resultof the operation, not into thec8y_ua_command_*fragment. Structured results are JSON documents inside this string. - The platform does not check the content of the fragment. If you misspell a property, the operation does not fail, but the gateway ignores the property.
A node ID must have one of the following forms. Otherwise, the operation fails with a failureReason that starts with Internal error: Invalid string representation of a nodeId:.
ns=<int>;i=<int> i=<int>
ns=<int>;s=<string> s=<string>
ns=<int>;g=<guid> g=<guid>
ns=<int>;b=<base64> b=<base64>
nsu=<namespace_uri>;<identifier>
SKILLS:
ROLES & PERMISSIONS:
- To check the device: ROLE_INVENTORY_READ
- To create operations: ROLE_DEVICE_CONTROL_ADMIN
- To read operations: ROLE_DEVICE_CONTROL_READ
The examples use <device_id> for the managed object ID of the OPC UA server or of a generated device, and <operation_id> for the ID of an operation.
To check the target device
Send a Retrieve a specific managed object request with the parents of the device:
GET /inventory/managedObjects/<device_id>?withParents=true
The platform returns HTTP 200 and the managed object. Check the following properties:
- The
typeisc8y_OpcuaServer, or the device is a device that a device protocol generated below a server. - One of the entries in
deviceParents.referenceshas amanagedObjectof typec8y_OPCUA_Device_Agent. This is the gateway that executes the operations.
If the type is c8y_OPCUA_Device_Agent, the ID is the ID of the gateway. Use the ID of one of its servers instead. If no parent is a gateway, no gateway executes the operations. Do not create them.
To read the values of nodes
Send a Create an operation request with the fragment c8y_ua_command_ReadValue:
POST /devicecontrol/operations
Content-Type: application/json
Accept: application/json
{
"deviceId": "<device_id>",
"description": "Read value",
"c8y_ua_command_ReadValue": {
"nodes": ["ns=2;s=MyLevel"],
"timestampsToReturn": "Both"
}
}
The fragment has the following properties:
nodes: The IDs of the nodes to read. Mandatory.timestampsToReturn: The timestamps to return with each value:Source,Server,Both, orNeither. The default isBoth. The values are case-sensitive.ranges: An index range to read a part of an array value, for example"0:1"for the first two elements of a one-dimensional array, or"0:1,0:1"for a two-dimensional array.maxAge: The maximum age of the value. With0, the default, the server reads a new value from the data source.
The platform returns HTTP 201 and the operation with its id and "status": "PENDING". Note the id as <operation_id> and read the result. If the platform returns HTTP 422, the device does not exist, or no gateway is above it.
See Reading the value of a node/nodes for details.
To read the attributes of a node
To read one attribute of one or more nodes, send a Create an operation request with the fragment c8y_ua_command_ReadAttribute:
POST /devicecontrol/operations
Content-Type: application/json
Accept: application/json
{
"deviceId": "<device_id>",
"description": "Read attribute",
"c8y_ua_command_ReadAttribute": {
"nodes": ["ns=3;s=FloatArray"],
"attribute": "13",
"ranges": "0:1"
}
}
The attribute is the ID of the attribute according to the OPC UA specification, as a string. "13" is the value attribute. The nodes, ranges, maxAge, and timestampsToReturn properties work as for reading values.
To read all attributes of one node, use the fragment c8y_ua_command_ReadNodeAttributes with the ID of the node:
"c8y_ua_command_ReadNodeAttributes": {
"node": "ns=2;s=MyEnumObject"
}
The platform returns HTTP 201 and the operation. See Reading an attribute and Reading all attributes of a node for details.
To write the values of nodes
Send a Create an operation request with the fragment c8y_ua_command_WriteValue:
POST /devicecontrol/operations
Content-Type: application/json
Accept: application/json
{
"deviceId": "<device_id>",
"description": "Write values",
"c8y_ua_command_WriteValue": {
"values": {
"ns=3;s=LocalizedText": {
"value": "This is a localized text"
},
"ns=3;s=Double": {
"value": "3.14159"
}
}
}
}
The values map contains one entry per node. The key is the node ID. Each entry has the following properties:
value: The value to write, as a string, also for numbers. Mandatory.ranges: An index range to write a part of an array value, for example"0:1"for the first two elements.
The platform returns HTTP 201 and the operation. See Write value for details.
To call a method
-
Read the description of the method. Send a Create an operation request with the fragment
c8y_ua_command_GetMethodDescriptionOperationand the ID of the method node:POST /devicecontrol/operations Content-Type: application/json Accept: application/json { "deviceId": "<device_id>", "description": "Get method description", "c8y_ua_command_GetMethodDescriptionOperation": { "nodeId": "ns=2;s=MyMethod" } }Read the result of the operation. It contains the
inputArgumentsandoutputArgumentsof the method, each withname,dataType, anddataTypeId, and theparentNodeIdof the method. -
Call the method. Send a Create an operation request with the fragment
c8y_ua_command_CallMethodOperation:POST /devicecontrol/operations Content-Type: application/json Accept: application/json { "deviceId": "<device_id>", "description": "Call method", "c8y_ua_command_CallMethodOperation": { "request": { "nodeId": "ns=2;s=MyMethod", "arguments": [ { "name": "Operation", "dataType": "String", "dataTypeId": "i=12", "value": "pow" }, { "name": "Parameter", "dataType": "Double", "dataTypeId": "i=11", "value": "5" } ] } } }Send one entry in
argumentsper input argument, in the order of the method description. Each entry needs thedataType, thedataTypeId, and thevalueas a string. For a method without input arguments, omitarguments. Send only thenodeIdof the method. The gateway finds the object of the method from the method description. Do not copy theparentNodeIdof the description into the request.The optional property
parseResponseinrequestcontrols whether the gateway converts the output values to JSON. The default istrue.
When the call is SUCCESSFUL, the result contains a statusCode and a result array with the output arguments and the values that the server returns. See Get method description and Call method for details.
To read the result of an operation
Send a Retrieve a specific operation request:
GET /devicecontrol/operations/<operation_id>
Repeat the request until the status is SUCCESSFUL or FAILED, as described in Send an operation to a device. Then parse the JSON string in c8y_Command.result. For a value read, the result looks as follows:
{
"results": {
"ns=2;s=MyLevel": {
"13": {
"value": {
"value": 77.0
},
"statusCode": 0,
"sourceTimestamp": 1566572540173,
"serverTimestamp": 1566573849897
}
}
}
}
The keys of results are the node IDs and the attribute IDs. A statusCode of 0 means Good. The result of c8y_ua_command_ReadNodeAttributes contains the attributes by name instead, for example Value, DataType, and DisplayName.
The result of a write is a write response with one status code per node in its results array. The gateway sets the operation to SUCCESSFUL as soon as the server answers. Check each status code: a status other than Good means that the write of this node failed.
If the status is FAILED, the failureReason describes why, for example:
Internal error: UA Server is not resolvable / not connected!: The gateway runs, but it is not connected to the OPC UA server.Operation is not supported!: The name of thec8y_ua_command_*fragment is misspelled.Error parsing operation JSON! This operation is not supported.: A property of the fragment has the wrong JSON type.Agent operation queue is full!: The gateway is busy. Create the operation again later.
If the operation stays PENDING, no gateway executes operations for the device, for example because the gateway is offline. To cancel the operation, see To cancel a pending operation.
To set an expiration time
To make sure that the gateway executes an operation only until a given time, add an expirationTime in ISO 8601 format inside the fragment, not at the top level of the operation:
"c8y_ua_command_ReadValue": {
"nodes": ["ns=3;s=FloatArray"],
"expirationTime": "2027-01-31T15:00:00.000Z"
}
The gateway checks the time when it picks up the operation. If the time has passed, the operation fails with Operation expired. The read value, read attribute, read node attributes, write value, and call method operations accept an expiration time. Other operations, for example the get method description operation, fail with Expiration not supported for this operation type. See Expiring operations.