SAES Neg Power MQTT Bridge (Unofficial)
Standalone MQTT monitoring and explicit activation control for SAES NEG POWER
MINI. No PowerSupply or pressure-gauge inheritance. A private internal
adapter translates MQTT commands into Modbus TCP or RS232/RTU transactions.
Connecting, disconnecting, restarting the service and recovering a connection
never automatically start or stop heating.
The manual documents the interfaces but omits the register map. The implementation uses a map recovered from the supplied manager software; see docs/protocol.md for evidence, defaults, units and unresolved details. Tests use local dummy devices. This has not been deployed or qualified against a physical NEG controller.
Install from this directory with python3 -m pip install . or python3 -m pip install pysaes-neg-power-tspspi. Copy
the example configuration, give the service
user ownership, and use mode 0600. Create the configured state_directory owned
by that same user with mode 0700. Run:
negpowermqtt --config /path/to/negpowermqtt.conf
Each device entry gets an independent polling/reconnect worker. Add additional
entries with unique names and endpoints for additional controllers. For serial,
replace device_host with serial_port; optional baudrate, parity, and
stopbits default to 38400, N, 1. Ethernet defaults to port 502 and unit 1;
the configured device values must agree with the front panel.
Control defaults to disabled. Set control_enabled: true for a device when
using external publishers to control it. Queries and monitoring work while
control is disabled. No hardware writes have been performed during development.
MQTT topics
For base /lab and name neg1:
| Topic | Direction | Payload |
|---|---|---|
/lab/neg/neg1/state |
Service publishes | Timestamp, online flag, identity, status, settings |
/lab/neg/neg1/availability |
Service publishes | Online flag and timestamp; MQTT last will also reports disconnect |
/lab/neg/neg1/command |
External publisher sends | JSON command envelope below |
/lab/neg/neg1/result |
Service publishes | Request id, result, timestamp, details/readback |
Publications use QoS 1 and are not retained. Periodic state is refreshed every 10 seconds by default. Query requests trigger a fresh snapshot. An offline device sends availability=false and retries; no cached state is substituted. Check timestamps; broker outages can delay or duplicate MQTT deliveries.
Commands require a unique id, an action, and Unix issued_at/expires_at values
with validity at most 60 seconds. Publish with retain=false. Retained
subscription deliveries, expired/future commands, malformed settings and unknown
actions/fields are rejected. No topic permits arbitrary register access.
Actions:
query: obtain a fresh state/settings snapshot.set_settings: set and verify voltage, ramp, hold and maximum temperature; require standby; leave output state unchanged.start: set and verify the complete supplied settings, check standby, alarms, thermocouple and current temperature, then start the controller's ramp.stop: explicitly request output stop and return status readback.
start executes the controller's voltage ramp and hold. The maximum temperature
is a controller cutoff, not a PID temperature target. A valid thermocouple and
firmware support are required. The service does not create pump recipes, infer
settings from pump type, clear alarms, or resume a ramp after reboot. Use the
activation parameters specified for the actual pump. Hold zero explicitly selects
indefinite operation; stopping the daemon does not terminate it.
Example start payload (timestamps must be generated when publishing):
{
"id": "activation-unique-id",
"action": "start",
"issued_at": 1700000000,
"expires_at": 1700000030,
"settings": {
"voltage_V": 8.8,
"ramp_minutes": 15,
"hold_minutes": 60,
"max_temperature_K": 573
}
}
These settings demonstrate the schema; they are not a recommended pump recipe. Temperature is an integer kelvin value on the wire (573 K is approximately 300 degrees Celsius). State also reports the measured temperature in Celsius.
completed means a query or settings transaction completed with readback.
accepted means start/stop was acknowledged and status was read back; it does
not mean activation finished or the temperature cutoff was physically verified.
rejected means the service did not accept the command. uncertain means writes
may have executed or settings may be partial; query state before deciding what
to do next. An uncertain start is never resent automatically.
The SQLite journal records each accepted request id before its first possible write. Exact duplicates return the saved result without replay; reusing an id with different content is rejected. This survives service restarts and lost result delivery. An interrupted claimed request remains uncertain. Do not delete the journal to retry a command. Offline commands are rejected, not saved for execution when the device returns. Commands queued across a broker reconnect are discarded. A retry uses the same id, timestamps and payload; an expired retry is rejected. Only issue a new control id after reconciling the device state.
The broker must authenticate publishers and enforce topic permissions: service accounts subscribe only to their command topics and publish only state, availability and result; observer accounts read those publications; control accounts publish only intended command topics. The service cannot identify a publisher from a normal MQTT message, so access control belongs at the broker. Use one command owner per controller; front-panel or other software changes can race separate Modbus transactions. MQTT delivery is not a real-time interlock; use the controller's hardware protections.
See examples/publish_command.py for an external publisher using its own private credentials file. A FreeBSD rc.d template is provided at service/negpowermqtt; no services or users have been installed by creating this repository.
Development
PYTHONPATH=src python3 -m pytest -q
python3 -m build
Tests cover TCP/RTU framing, settings validation/readback, temperature/start checks, retained/expired commands, persistent deduplication, uncertain writes, multiple devices, and real Paho MQTT interaction with a local dummy broker.
Metadata
Release files for pysaes-neg-power-tspspi 0.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 | |
|---|---|---|---|
| pysaes_neg_power_tspspi-0.0.1.tar.gz | 25.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pysaes_neg_power_tspspi-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.5 kB
Release files / pysaes_neg_power_tspspi-0.0.1.tar.gz
| Download URL | pysaes_neg_power_tspspi-0.0.1.tar.gz |
|---|---|
| Size | 25.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fd28fcce570408b324b2974f091561b1aff3c33f69103432e299a7c6d101bf51
|
|
BLAKE2b-256 checksum How to use checksums |
72a46a2200dd3525c15bb0f8faff11f43d2a0c81496d27512603ad43af090403
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.11
|
Release files / pysaes_neg_power_tspspi-0.0.1-py3-none-any.whl
| Download URL | pysaes_neg_power_tspspi-0.0.1-py3-none-any.whl |
|---|---|
| Size | 16.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6eac5b63dc0d9346e0679a735ec2dd3b6595d50353ad719414537bcb3948decb
|
|
BLAKE2b-256 checksum How to use checksums |
4d1ea80140582643d90f4efc205267ecafb756e680181b1c483e42d90ba431b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.11.11
|