Skip to content

feat(mqtt-bridge): write data points from MQTT, then read the device - #676

Merged
groupsky merged 3 commits into
mainfrom
feat/mqtt-bridge-write-on-set
Oct 21, 2026
Merged

groupsky merged 3 commits into
mainfrom
feat/mqtt-bridge-write-on-set

Conversation

@groupsky

@groupsky groupsky commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Every bus still on the old reader writes to a device over MQTT, so none of them can move to the bridge until it can write.

What it does

  • Option: allowWrites: true on a device (default false). Without it, every write message gets an error result and nothing is written, so a meter is not written by mistake.
  • Write topic: {topicPrefix}/{deviceId}/set, one per enabled device. Payload: a JSON object { "<dataPointId>": value, ... }.
  • Checks first: the device allows writes, and every point exists and is writable. If one fails, nothing is written.
  • Same turn on the bus as the polls: the writes and the read after them run as one job in the per-connection queue of the polling scheduler. So a write never runs in the middle of a poll, and the transport adds the same pause between devices.
  • Order: points are written in message order. A driver error stops the writes: the points after it are not written; the points before it stay written, and the result lists them in written.
  • Read at once: after the writes (also after a driver error, so the real state shows), the bridge reads the device and publishes on {topicPrefix}/{deviceId}/data. The read covers the points a poll reads. A failed read here is left to the next poll.
  • Result: on {topicPrefix}/{deviceId}/set/result, QoS 0, not retained: { deviceId, timestamp, ok: true, values } or { deviceId, timestamp, ok: false, error, values, written }. The error names the point and why; written is [] when nothing was written.
  • Retained messages: the stored copy that the broker sends again at each subscribe is ignored (with a warning), so an old command is not written again at every start or reconnect.

All of it is in the mqtt-bridge README, section "Writing Data Points".

Notes

  • The issue says a value the driver rejects means "the other points are not written". The driver interface has no way to check a value without writing it, so the points before the failing one stay written. The result lists them in written, and the README says so.
  • A written point with pollType: 'static' is not read again; /data keeps the kept value. No driver has a writable static point.

Tests

  • Unit (index.writes.test.ts, polling-scheduler.writes.test.ts): one point; several points in order; unknown point; read-only point; device without allowWrites; driver error (also on the first point), with written and the /data after the partial write; read and publish right after the write; a write waits for a running poll on the same port; retained message; invalid payloads.
  • End-to-end (write.integration.test.ts): real broker and MQTT client, real transport, TCP emulator. Writes a register over /set, sees the new value on /data and ok: true on the result.

Refs #675

Every bus still on the old reader writes to a device over MQTT, so none
of them can move to the bridge until it can write.

A JSON object on {topicPrefix}/{deviceId}/set is written to a device
with allowWrites: true, in message order, in the same turn on the bus
as the polls. All points are checked first. The device is then read at
once and published on /data, and the result goes to /set/result. The
stored copy of a retained message is ignored, so it is not written
again at every start or reconnect.

Refs #675
@pkg-pr-new

pkg-pr-new Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@ya-modbus/cli

npm i https://pkg.pr.new/@ya-modbus/cli@676

@ya-modbus/device-profiler

npm i https://pkg.pr.new/@ya-modbus/device-profiler@676

@ya-modbus/driver-dds024mr

npm i https://pkg.pr.new/@ya-modbus/driver-dds024mr@676

@ya-modbus/driver-dds519mr

npm i https://pkg.pr.new/@ya-modbus/driver-dds519mr@676

@ya-modbus/driver-ex9em

npm i https://pkg.pr.new/@ya-modbus/driver-ex9em@676

@ya-modbus/driver-loader

npm i https://pkg.pr.new/@ya-modbus/driver-loader@676

@ya-modbus/driver-or-we-514

npm i https://pkg.pr.new/@ya-modbus/driver-or-we-514@676

@ya-modbus/driver-or-we-516

npm i https://pkg.pr.new/@ya-modbus/driver-or-we-516@676

@ya-modbus/driver-sdk

npm i https://pkg.pr.new/@ya-modbus/driver-sdk@676

@ya-modbus/driver-sdm630

npm i https://pkg.pr.new/@ya-modbus/driver-sdm630@676

@ya-modbus/driver-types

npm i https://pkg.pr.new/@ya-modbus/driver-types@676

@ya-modbus/driver-xymd1

npm i https://pkg.pr.new/@ya-modbus/driver-xymd1@676

@ya-modbus/emulator

npm i https://pkg.pr.new/@ya-modbus/emulator@676

@ya-modbus/mqtt-bridge

npm i https://pkg.pr.new/@ya-modbus/mqtt-bridge@676

@ya-modbus/transport

npm i https://pkg.pr.new/@ya-modbus/transport@676

ya-modbus

npm i https://pkg.pr.new/ya-modbus@676

ya-modbus-bridge

npm i https://pkg.pr.new/ya-modbus-bridge@676

ya-modbus-emulator

npm i https://pkg.pr.new/ya-modbus-emulator@676

ya-modbus-profile

npm i https://pkg.pr.new/ya-modbus-profile@676

commit: ceafaf1

After a driver error the points before it stay written on the device,
and the result did not say which ones. Every failed result now has
`written`: the IDs the driver wrote before the error, in message order,
or [] when nothing was written.

Refs #675
The read after a write reads only the points a poll reads, so the
README must not promise the full state of the device. A timeout can
also leave the failed point written without it being in `written`.
The tests now check `written` for a device that does not allow writes
and for a driver error on the first point.

Refs #675
@groupsky
groupsky merged commit eec51fb into main Oct 21, 2026
13 checks passed
@groupsky
groupsky deleted the feat/mqtt-bridge-write-on-set branch October 21, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant