Skip to main content
6-Contributor
July 6, 2026
Question

Python wrapper for C SDK for ThingWorx

  • July 6, 2026
  • 0 replies
  • 11 views

Most ThingWorx edge agents are written in C, Java, or .NET. But a lot of edge logic today — data science, protocol bridges, quick prototypes — lives in Python. This post shows how to connect a Python program to ThingWorx using a thin Python wrapper around the ThingWorx C SDK, so you get the performance and protocol support of the native twCSdk library with a Pythonic API on top.

What the wrapper is

The wrapper (thingworx-python) is a pure-Python package that talks to the native ThingWorx C SDK through Python's standard-library ctypes. It ships two layers:

  • high-level API — ThingWorxClientThingDataShapeInfoTableBaseType, plus decorators for properties/services/events; and
  • low-level 1:1 binding to the C SDK (twApi_*twPrimitive_*twInfoTable_*, …) for anything the high-level API doesn't expose.

It has zero pip dependencies — only ctypes. The native library itself is not shipped; you provide the twCSdk build for your platform.

Where to get it

The full project — wrapper, examples, and sample solutions — is available on GitHub:
https://github.com/thingworx-field-work/ThingWorx-Python-wrapper

Installing and importing

Install the package (from a wheel, a source checkout, or pip install -e .), then import what you need from the top-level package:

from thingworx import (
ThingWorxClient, Thing, DataShape, InfoTable,
BaseType, MsgCode, LogLevel, ThingWorxError,
)

Importing the package does not load the native library — that happens lazily the first time you construct a ThingWorxClient.

How communication with twCSdk.dll / libtwCSdk.so works

The Python code never speaks the ThingWorx AlwaysOn / WebSocket protocol itself. That is entirely the job of the native C SDK. The wrapper only marshals between Python and C:

  1. Loading the library. When you create a ThingWorxClient, a small loader locates the native library for your OS — twCSdk.dll (Windows), libtwCSdk.so (Linux), or libtwCSdk.dylib (macOS) — and opens it with ctypes. It searches, in order:
    1. the TWCSDK_LIB_PATH environment variable,
    2. the current working directory,
    3. the package directory (and its parents),
    4. the OS loader path (PATH / LD_LIBRARY_PATH / DYLD_LIBRARY_PATH).
    On Windows the SDK also needs its OpenSSL DLLs (libcrypto-3-x64.dlllibssl-3-x64.dll) in the same folder.
  2. Outbound calls (Python → platform). Each wrapper method calls the matching C function through ctypes — e.g. Thing.bind() calls twApi_BindThingwrite_property() builds a twPrimitive and calls twApi_PushSubscribedProperties. The C SDK then sends the WebSocket message to the platform. Python values are converted to C types (numbers, strings, InfoTables) on the way in.
  3. Inbound calls (platform → Python). When the platform reads/writes a property or invokes a service, the C SDK invokes a callback. The wrapper registers C-callable function pointers (ctypes.CFUNCTYPE) that trampoline straight into your Python handler, convert the C arguments to Python, and convert your return value back to a C result code.
  4. Errors. C functions return an integer status; the wrapper checks it and raises a mapped Python exception (ThingWorxError and subclasses) so you can use normal try/except.

Two rules that follow from this bridge: keep a reference to your client (letting it be garbage-collected tears down the SDK underneath you), and treat the C SDK as the owner of the socket — Python just drives it.

A complete example: simple_thing.py

The example below connects, registers one property with a handler, binds, and then streams a simulated temperature to the platform once per second.

import time, random
from thingworx import (ThingWorxClient, Thing, BaseType, MsgCode,
LogLevel, ThingWorxError)

PLATFORM_HOST, PLATFORM_PORT = "localhost", 8016 # 8016 = HTTP/WS, 443 = HTTPS/WSS
APP_KEY, THING_NAME = "your-app-key", "PyDemo"
current_temperature = 72.0

def main():
global current_temperature
use_encryption = (PLATFORM_PORT != 8016)

with ThingWorxClient(
host=PLATFORM_HOST, port=PLATFORM_PORT, app_key=APP_KEY,
gateway_name="PythonGateway",
encryption=use_encryption,
self_signed_ok=not use_encryption,
) as client:
client.set_log_level(LogLevel.WARN)
thing = Thing(client, THING_NAME)

# Handler for reads/writes coming FROM the platform.
@thing.property_handler("Temperature", BaseType.NUMBER,
description="Current temperature",
push_type="ALWAYS")
def handle_temperature(entity, prop_name, value, is_write):
global current_temperature
if is_write:
current_temperature = value
return MsgCode.SUCCESS

client.connect(timeout=10000, retries=5) # open the WebSocket
thing.bind() # register the Thing

while True: # push values UP to the platform
current_temperature += random.uniform(-1.5, 1.5)
try:
thing.write_property("Temperature", current_temperature)
except ThingWorxError as e:
print("write failed:", e)
time.sleep(1)

if __name__ == "__main__":
main()

What each part does:

  • ThingWorxClient(...) loads the native SDK and configures the connection (host, port, app key, TLS). Using it as a with block guarantees a clean disconnect and keeps the client alive for the whole session.
  • Thing(client, THING_NAME) represents a RemoteThing already defined in Composer. THING_NAME must match.
  • @thing.property_handler(...) registers the Temperature property and wires a callback the SDK calls when the platform reads or writes it. Returning MsgCode.SUCCESS acknowledges the request.
  • client.connect() opens the AlwaysOn WebSocket; thing.bind() registers the Thing so the platform knows it is online.
  • thing.write_property(...) pushes each new value up to the platform. Stop the loop with Ctrl+C.

Troubleshooting

A few first-run issues account for almost every support question:

  • OSError: Failed to load ThingWorx C SDK library 'twCSdk.dll' — the loader can't find the native library. Set TWCSDK_LIB_PATH to the folder that holds it, or run from that folder.
  • ImportError: DLL load failed while importing _bindings (Windows) — almost always the OpenSSL DLLs are missing. Put libcrypto-3-x64.dll and libssl-3-x64.dll next to twCSdk.dll.
  • connect() hangs or times out — usually TLS/cert mismatch. For dev servers use self_signed_ok=True; for the non-secure port 8016 use encryption=False.
  • Access violation right after start — the ThingWorxClient was garbage-collected. Assign it to a variable or use a with block.

Takeaways

The wrapper lets you write ordinary Python while the battle-tested C SDK handles the transport. Import from thingworx, point TWCSDK_LIB_PATH at your native library (with the OpenSSL DLLs on Windows), create a ThingWorxClient, define a Thing, and you are exchanging data with ThingWorx in a few dozen lines.