Node API syntax¶
The Node API is a thin dotted/bracketed/call-syntax layer on top of the same connection. Instead of carrying NodeId strings around, you navigate the address space with . (children) and [] (paths), and act on a node by calling it.
This page is a quick orientation. The full tour lives in the Node API usage; this page shows the three shapes you'll use most often and the connection between them and the high-level API.
This page walks through three steps:
- Browse with
.and[]. - Act on a node with
(). - Use the same syntax from sync and async code.
Info
This tutorials requires you to know how to create and connect a client. We assume a server is running on localhost as described in example server in the tutorials intro.
Browse with . and []¶
The dot operator triggers a server-side Browse for the named child. Children are resolved lazily and cached:
var = client.objects.DistillingSystem.Kettle.Temperature
method = client.objects.DistillingSystem.Start
Names are matched case-insensitively against BrowseName.
The result is a Node subclass — VariableNode, ObjectNode, MethodNode, … — depending on the child's NodeClass.
We can use [] in conjunction with a browse path — a /-separated string of <namespace-index>:<BrowseName> segments, per the OPC UA RelativePath string grammar:
[] is the right tool when a BrowseName is ambiguous — i.e. more than one child has the same name (case-insensitively).
. cannot pick a single target in that case; [] returns a list of matching targets that you disambiguate yourself. The distillery doesn't have any ambiguous names, but the mechanism is the same either way — index into the returned list:
nodes = client.objects["/1:DistillingSystem/1:Kettle"] # a list, even with one match
kettle = nodes[0] # pick the target
Tip
In a Python REPL, dir(client.objects) and <TAB> completion both run a browse and list the children — see Node API usage.
Putting it all together¶
from o6 import Client
with Client("opc.tcp://localhost:4840") as client:
var = client.objects.DistillingSystem.Kettle.Temperature
nodes = client.objects["/1:DistillingSystem/1:Kettle"]
Act on a node with ()¶
Once you have a node, the same () operator handles every interaction.
The distinction is by argument shape, not by method.
Reading a variable value becomes a simple call with no arguments:
Similarly writing the variable is a call with an appropriately typed argument:
Non-Value attributes are read by specifying the o6.AttributeId (or its string name) for the parameter attr:
Method nodes are called the same way — pass the input arguments positionally. The parent object is picked up automatically. Start takes no arguments and returns just a StatusCode:
Putting it all together¶
import o6
from o6 import Client
with Client("opc.tcp://localhost:4840") as client:
var = client.objects.DistillingSystem.Status.Setpoint
print(var())
var(90.0)
print(var())
status, = client.objects.DistillingSystem.Start()
print(status == o6.StatusCode.GOOD)
Use the same syntax for sync and async code¶
Every node call is awaitable. Sync and async use the same expressions — add or drop await:
import asyncio
import o6
from o6 import Client
async def main():
async with Client("opc.tcp://localhost:4840") as client:
# Browse
var = await client.objects.DistillingSystem.Status.Setpoint
# Read
value = await var()
# Write
await var(90.0)
# Call
status, = await client.objects.DistillingSystem.Start()
print(value, status == o6.StatusCode.GOOD)
asyncio.run(main())
The dotted navigation, the bracket navigation, and the call syntax are identical in both modes — only the await keyword changes.
What's next?¶
- Node API tutorial — the full reference, including interactive completion and the quick-reference table.
- Read / write value and Call a method — the same operations through the high-level API.