engiNIR-OPCUA
OPC UA for Python, from the lab bench to the plant floor: connect, serve and secure your devices in minutes.
OPC UA is how machines, instruments and control systems share data. engiNIR-OPCUA lets you speak it from Python, as a client that reads your PLC or as a server that puts your own device on the network, with the parts a real project needs already built in.
- Complete. Subscriptions, methods, alarms and conditions, history, file transfer, certificate management and the companion models of whole industries, in one library, as a client and as a server.
- Extended and hardened. Built on opcua-asyncio, brought up to OPC UA 1.05, extended with the server features listed below and hardened against the attacks known for OPC UA servers.
- Effortless. Encrypted from the first connection, set up with four commands, written in a few lines of async or plain blocking Python.
uv add enginir-opcua # or: pip install enginir-opcua
A secure connection in one minute
Create a server and a client, make them trust each other, add a user:
enginir-opcua init server srv --endpoint opc.tcp://0.0.0.0:4840/demo/
enginir-opcua init client cli
enginir-opcua pair srv/pki cli/pki
enginir-opcua user add srv/users.toml alice --role user
Serve a value:
import asyncio
from enginir_opcua import Server
from enginir_opcua.config import ServerConfig
async def main() -> None:
server = Server(config=ServerConfig.from_toml("srv/server.toml"))
await server.init()
idx = await server.register_namespace("urn:example:demo")
temperature = await server.nodes.objects.add_variable(idx, "Temperature", 21.5)
async with server:
while True:
await asyncio.sleep(1)
await temperature.write_value(await temperature.read_value() + 0.1)
asyncio.run(main())
Read it from another program, as alice (password in ENGINIR_OPCUA_PASSWORD):
import asyncio
from enginir_opcua import Client
async def main() -> None:
async with Client.quickstart("opc.tcp://localhost:4840/demo/", pki="cli/pki", username="alice") as client:
idx = await client.get_namespace_index("urn:example:demo")
temperature = await client.nodes.objects.get_child(f"{idx}:Temperature")
print(await temperature.read_value())
asyncio.run(main())
That is a signed and encrypted channel, two certificates that trust each other and nobody else, and a user whose
role decides what alice may change. Connecting to a server you didn't set up works the same way: the first attempt
prints the enginir-opcua trust command for each side.
What you can build
Get machine data into Python
Read a production line into a script, a notebook or a database, with the blocking API if you don't want async:
from enginir_opcua.sync import Client
with Client.quickstart("opc.tcp://192.168.0.10:4840", pki="pki") as client:
line = [client.get_node(f"ns=3;s=Line1.{name}") for name in ("Temperature", "Pressure", "Speed")]
print(client.read_values(line))
Subscriptions deliver every change as it happens, history reads fetch the past, and the server's own data types
arrive as Python objects (load_data_type_definitions).
Turn an instrument into an OPC UA device
Companion specifications define how a kind of device looks on OPC UA, so every client already knows where to find its serial number, its state and its data. Import them and create your device as an instance, with all mandatory parts filled in. A spectrometer by the Analyser Devices (ADI) model:
await server.import_xml("Opc.Ua.Di.NodeSet2.xml")
await server.import_xml("Opc.Ua.Adi.NodeSet2.xml")
di = await server.get_namespace_index("http://opcfoundation.org/UA/DI/")
adi = await server.get_namespace_index("http://opcfoundation.org/UA/ADI/")
spectrometer = await server.nodes.objects.add_object(idx, "Spectrometer", objecttype=ua.NodeId(1011, adi))
serial = await spectrometer.get_child([f"{di}:Identification", f"{di}:SerialNumber"])
await serial.write_value("SN-0001")
spectrum = await spectrometer.add_variable(idx, "Absorbance", [0.0] * 256)
The same works for lab devices (LADS), machines (Machinery) and every other model published as a NodeSet2 file. Example 07 builds the complete spectrometer.
Simulate a machine for testing
Your SCADA, MES or analytics code needs a machine to talk to, and the machine is in another country. Import the NodeSet2 file its vendor ships and serve it; your tests and your CI get the same address space:
server = Server.quickstart("opc.tcp://127.0.0.1:4840/machine/", pki="pki")
await server.init()
await server.import_xml("Machine.NodeSet2.xml")
async with server:
... # write test values, run the tests
Bridge a device that doesn't speak OPC UA
A scale on a serial port, a Modbus controller, a sensor with its own SDK: give each value a callback, and the device is asked only when a client reads:
weight = await device.add_variable(idx, "Weight", 0.0)
server.set_attribute_value_callback(weight.nodeid, lambda nodeid, attribute: ua.DataValue(scale.read_kg()))
Run it in production
- history of values and events, in memory or SQLite (example 06)
- alarms and conditions that operators acknowledge and confirm (example 12)
- files and directories that clients read and write (example 08)
- certificates and trust lists managed remotely by a security administrator (example 14)
- reverse connect, so a server inside a protected network dials out to its clients (example 16)
- diagnostics of sessions and subscriptions (example 13)
- one TOML file for the whole configuration, served with
enginir-opcua server --config server.toml
What it supports from the standard
engiNIR-OPCUA follows OPC UA 1.05 (OPC 10000) and accepts peers that speak 1.04.
| Part | Supported |
|---|---|
| 3, 5 Address space and information model | custom object, variable, event and data types; structures, unions, enumerations and OptionSets with their DataTypeDefinition; NodeSet2 import and export; diagnostics and model change events |
| 4 Services | discovery and discovery server, sessions, browse, read and write, methods, node management, subscriptions for data changes and events, subscription transfer |
| 6 Mappings | opc.tcp with the binary encoding; Basic256Sha256, Aes128_Sha256_RsaOaep and Aes256_Sha256_RsaPss; anonymous, user name and X.509 users; reverse connect |
| 9 Alarms and conditions | conditions, acknowledgeable and limit alarms, refresh |
| 10 Programs | program state machines |
| 11 Historical access | raw values and events, stored in memory or SQLite |
| 12 Certificate management | push management of certificates and trust lists |
| 14 PubSub | UADP over UDP (experimental) |
| 20 File transfer | files and directories |
| Companion specifications | DI, ADI, LADS, Machinery and the other models published as NodeSet2 |
What is not supported yet, such as the ECC policies and WebSockets, is listed under Supported features and limits.
Coming from asyncua? Most code moves over by changing the import; the migration guide lists the rest.
Command line
The same tool sets up certificates and users, serves a configuration and inspects any server:
enginir-opcua discover -u opc.tcp://192.168.0.10:4840 # endpoints and policies
enginir-opcua ls -u opc.tcp://192.168.0.10:4840 --pki-dir pki -n i=85 # browse the Objects folder
enginir-opcua read -u opc.tcp://192.168.0.10:4840 --pki-dir pki -n "ns=3;s=Line1.Temperature"
enginir-opcua subscribe -u opc.tcp://192.168.0.10:4840 --pki-dir pki -n "ns=3;s=Line1.Temperature"
enginir-opcua write -u opc.tcp://192.168.0.10:4840 --pki-dir pki -n "ns=3;s=Line1.Setpoint" 185
No server at hand? enginir-opcua server --pki-dir demo-pki --populate starts one with sample nodes.
Documentation
https://enginir-opcua.readthedocs.io/
- Quick setup: profiles, configuration files, users and roles, trusting certificates
- Client, Server and the blocking API
- Examples: 17 runnable programs
- Supported features and limits
- Migrating from asyncua and the CHANGELOG
Contributing
uv sync && git submodule update --init --depth 1 nodeset
uv run pytest -q -m "not slow"
Conventions for code and tests: AGENTS.md and tests/README.md.
License
LGPL-3.0-or-later; see COPYING, COPYING.GPL and NOTICE. The original library was written by Olivier Roulet-Dubonnet and the opcua-asyncio contributors; this fork was made on 2026-10-25.
Metadata
Release files for enginir-opcua 3.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| enginir_opcua-3.0.1.tar.gz | 1.1 MB | With provenance | Details for enginir_opcua-3.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| enginir_opcua-3.0.1-py3-none-any.whl | Python 3 | none | any | Details for enginir_opcua-3.0.1-py3-none-any.whl |
Total release size: 2.2 MB
Release files / enginir_opcua-3.0.1.tar.gz
| Download URL | enginir_opcua-3.0.1.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
98cf100712b216b5fc853a85eea1532fd894b2d050f7c12b282dea8a02a06ba4
|
|
BLAKE2b-256 checksum How to use checksums |
e45bc6599fe0d7d8fc0f624a027267a403fabebcdb91126baad488e0f01d7b21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logRelease files / enginir_opcua-3.0.1-py3-none-any.whl
| Download URL | enginir_opcua-3.0.1-py3-none-any.whl |
|---|---|
| Size | 1.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
36ca9c833b3350e9f314b27900b20ffec3d1a3e6a3e248c6c2bc2fca6cbf5dac
|
|
BLAKE2b-256 checksum How to use checksums |
3a738d54cb8e140a2a19999f5c0ecdc83fffd72c85a84cb77e50bb9b36840427
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logSecurity for enginir-opcua 3.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for enginir_opcua-3.0.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 1, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.