Skip to main content

engiNIR-OPCUA

PyPI Python CI Interoperability Documentation License: LGPL-3.0-or-later

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/

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)

Source distribution for enginir-opcua 3.0.1
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)

Table of built distributions (wheels) for enginir-opcua 3.0.1
File Interpreter ABI Platform Actions
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page