Loading Python Classes as Models
Build model logic directly in Python and plug it into SPX via YAML/JSON — an alternative to fully declarative models.
Not every behavior is pleasant to express in YAML/JSON. When the core of your model is easier to write in Python, you can keep the system structure (models/instances/connections) declarative, while implementing the behavior in a plain Python class and importing it into SPX. This guide walks through a small but complete “temperature sensor” example and shows how the Python class is wired to attributes and methods, and how you can later expose those attributes over Modbus.
When to use it
Your logic is complex enough that Python is clearer than a purely declarative spec.
You want to reuse existing Python libraries or your own tested code.
You still need portable system topology (YAML/JSON) for deployment, tests, and tooling.
1) Write the Python class
Create extensions/py_temp_sensor.py and implement the behavior.
The class below keeps an internal temperature, exposes it via a temperature property, and defines a small tick() helper that the model can call on each run().
# extensions/py_temp_sensor.py
import math
import time
class PyTempSensor:
"""
Minimal sensor logic implemented in Python.
- Keeps an internal temperature value
- Exposes a property 'temperature' used by SPX
- Provides a 'tick' helper to update the internal state
"""
def __init__(self, start: float = 25.0, drift: float = 0.0):
self._t = start
self._drift = drift
self._t0 = time.time()
# Property used by SPX to read/write the attribute
@property
def temperature(self) -> float:
return self._t
@temperature.setter
def temperature(self, val: float):
self._t = float(val)
# Optional helper you can call from actions/logic
def tick(self) -> float:
# Example: a tiny sinusoid + linear drift
now = time.time() - self._t0
self._t = self._t + self._drift + 0.05 * math.sin(now)
return self._tWhat to notice
You do not need to inherit from any SPX base class. A plain Python class is fine.
SPX will bind the model’s attributes to your class’ property/getter/setter.
Any public method (like
tick) can be mapped to SPX lifecycle methods (e.g.,run).
2) Declare it in YAML (or JSON)
Now reference that Python class from a model definition.
The YAML below defines a PySensorModel with one attribute and uses import to load PyTempSensor. It maps the model’s temperature attribute to the class’ temperature property and maps the model’s run method to the class’ tick function.
How the mapping works
importuses the path to the Python file as a key. SPX loads the module and instantiatesclass: PyTempSensor.init.kwargsare forwarded to__init__(so you can configure defaults from YAML/JSON).Under
attributesyou bind each SPX attribute to either:a property on your class:
{ property: temperature }, orexplicit accessors:
{ getter: read_temp, setter: set_temp }.
Under
methodsyou can map model methods to your Python methods, e.g.,run: tick. When the instance runs, SPX will callPyTempSensor.tick().
Internally this is handled by the
python_fileimporter. Wiring happens duringprepare().
3) Create a model and instance, then run
The next snippet shows how to use the HTTP wrapper to push the YAML to the server and create a running instance. We also disable background polling to keep the sample deterministic and call prepare() to ensure the importer wires up the attributes/methods before use.
What happens here
The model is created with the
importblock as defined above.The instance
test_pt_100_pyis added, referencing that model.prepare()loads the Python file, instantiatesPyTempSensor, and connects:the model’s
temperatureattribute ⇄ thetemperatureproperty,the model’s
runmethod ⇄ thetick()function.
From now on, each
instance.run()callsPyTempSensor.tick().
4) Sample and visualize the signal
This final code collects samples by repeatedly calling run() (therefore invoking tick() under the hood) and plots temperature over time with Plotly.
Key takeaways
instance.run()→ mapped toPyTempSensor.tick().The
temperatureattribute you read is the property on your class.You can choose your own update cadence and time base.
5) (Optional) Expose your Python‑backed attributes over Modbus
You can build hybrid models: keep the logic in Python, and expose selected attributes via industrial protocols such as Modbus.
The YAML below adds a communication section to the same model and publishes temperature as a holding register with modbus_slave (recommended Modbus TCP server adapter).
Exact keys and encoding options may vary across SPX releases. See the dedicated Modbus guide for full details. The snippet demonstrates the typical shape.
What this adds
A Modbus TCP server is started for the instance.
The
temperatureattribute is mapped to a pair of holding registers.You can extend the mapping with more attributes or different encodings.
Advanced options
Getter/Setter mapping Prefer explicit functions instead of a property:
Multiple classes Import several classes by listing multiple module entries under
import.Plain classes vs.
SpxComponentThe importer supports both. ForSpxComponentsubclasses, SPX passes context automatically.
Summary
Keep topology in YAML/JSON (models, instances, connections), but implement behavior in Python where it’s more natural.
Use the
import/python_filebridge to map attributes and methods to your class.Call
prepare()before use so the importer wires everything up.(Optional) Expose the same attributes over Modbus or other protocols for integration.
Last updated

