Python wrapper for C SDK for ThingWorx
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:
- a high-level API —
ThingWorxClient,Thing,DataShape,InfoTable,BaseType, plus decorators for properties/services/events; and - a 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:
- Loading the library. When you create a
ThingWorxClient, a small loader locates the native library for your OS —twCSdk.dll(Windows),libtwCSdk.so(Linux), orlibtwCSdk.dylib(macOS) — and opens it withctypes. It searches, in order:- the
TWCSDK_LIB_PATHenvironment variable, - the current working directory,
- the package directory (and its parents),
- the OS loader path (
PATH/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH).
libcrypto-3-x64.dll,libssl-3-x64.dll) in the same folder. - the
- Outbound calls (Python → platform). Each wrapper method calls the matching C function through
ctypes— e.g.Thing.bind()callstwApi_BindThing,write_property()builds atwPrimitiveand callstwApi_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. - 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. - Errors. C functions return an integer status; the wrapper checks it and raises a mapped Python exception (
ThingWorxErrorand subclasses) so you can use normaltry/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 awithblock 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_NAMEmust match.@thing.property_handler(...)registers theTemperatureproperty and wires a callback the SDK calls when the platform reads or writes it. ReturningMsgCode.SUCCESSacknowledges 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 withCtrl+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. SetTWCSDK_LIB_PATHto 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. Putlibcrypto-3-x64.dllandlibssl-3-x64.dllnext totwCSdk.dll.connect()hangs or times out — usually TLS/cert mismatch. For dev servers useself_signed_ok=True; for the non-secure port8016useencryption=False.- Access violation right after start — the
ThingWorxClientwas garbage-collected. Assign it to a variable or use awithblock.
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.

