Skip to content

fix: MQTT listener credentials and live sync of machine counts - #336

Merged
jakub-przepiora merged 3 commits into
Mes-Open:developfrom
dariuszprzepiora:fix/mqtt-auth-and-live-sync
Oct 4, 2026
Merged

jakub-przepiora merged 3 commits into
Mes-Open:developfrom
dariuszprzepiora:fix/mqtt-auth-and-live-sync

Conversation

@dariuszprzepiora

@dariuszprzepiora dariuszprzepiora commented Oct 2, 2026 •

Copy link
Copy Markdown

Summary

Two small fixes found while running OpenMES with a real MQTT piece counter (ESP8266 + laser sensor → Mosquitto → mqtt-listener → work order quantity) on a Raspberry Pi.

1. MQTT credentials and TLS were never applied (MqttListenCommand::buildSettings())

php-mqtt/client's ConnectionSettings is immutable — every setter returns a modified clone. buildSettings() called setUsername(), setPassword(), setUseTls() and setTlsCertificateAuthorityFile() without assigning the result, so none of them reached the connection. Against a broker with anonymous access disabled, the listener was rejected as not authorised, with nothing indicating that the credentials had not been sent. The fix assigns each result back to $settings.

2. Machine counts from MQTT did not live-sync to the browser (docker-compose.yml)

mqtt-listener is pinned to BROADCAST_CONNECTION: log. CollectionChanged is ShouldBroadcastNow, so it is dispatched by the process that writes the row — for quantities coming from MQTT that is mqtt-listener. The deltas went to the log instead of Reverb, so the work order list and dashboard only showed new counts after a manual reload. The service now uses reverb with the same REVERB_* settings as backend and queue-worker.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • Other:

Related issue

None filed.

Testing

Verified manually on a running deployment (Raspberry Pi 3B, Mosquitto with anonymous access disabled, MQTT mapping $.licznik → update_work_order_qty, qty_increment: false):

  • with fix 1 the listener authenticates (u'openmes' in the broker log) and messages are stored with status ok; without it the broker rejects the connection;

  • with fix 2 produced_qty updates live in the admin work order list; without it a reload was required.

  • Tested manually in browser

  • php artisan test passes — not run; neither change touches code paths covered by the suite

  • Tested as Operator / Supervisor / Admin role (if UI change) — not a UI change

Checklist

  • I have signed the CLA
  • No .env secrets committed
  • Migration added if schema changed — n/a
  • $fillable updated if new model columns added — n/a
  • No raw SQL with user input
  • CSRF protection in place for any new forms — n/a
  • composer audit clean — no dependency changes

Notes (not in this PR)

  • The admin work order detail page (Pages/admin/work-orders/Show.jsx) has no live refresh, unlike the list and dashboard, so counts there still need a reload.
  • backend is the only service that does not receive APP_KEY from the host .env; its entrypoint generates its own key, while the sidecars use the host one and share bootstrap/cache. In our setup this produced DecryptException: The MAC is invalid in mqtt-listener for the MQTT password saved in the panel. Not fixed here because simply passing an empty APP_KEY to backend would break installs that rely on the generated key — worth a separate issue.

🤖 Generated with Claude Code


Added after review (jakub-przepiora)

Correction to point 2 above: queue-worker has no REVERB_* settings and no BROADCAST_CONNECTION either — the original wording here was wrong. Checking that turned up the same defect in two more services, so this branch now closes the whole family rather than one case of it.

3. modbus-poller and queue-worker had the same problem, differently shaped

Neither sets BROADCAST_CONNECTION, so both fall back to the config default — reverb — but with no REVERB_HOST, REVERB_PORT or REVERB_SCHEME. config/reverb.php defaults those to an empty host on port 443 over https, which resolves to nowhere.

It is invisible by design: CollectionBroadcaster::safeBroadcast() reports the exception and swallows it, because a broadcast must never break the write that triggered it. The row is stored and the browser keeps showing the old value until someone reloads — exactly the symptom this PR started from, one layer along.

  • modbus-poller writes machine counter readings, so a Modbus counter had the same live-sync gap as the MQTT one
  • queue-worker sends every queued ShouldBroadcast event
  • opcua-gateway is unaffected — it posts to the backend API, and the backend both writes the row and broadcasts it

4. Unit test for buildSettings()

tests/Unit/Connectivity/MqttConnectionSettingsTest.php — four cases: credentials reach the connection, an anonymous broker is left without them, TLS and its CA file are applied, and the saved timings survive. Two of the four fail without the immutability fix in this branch, which is what makes them worth having; buildSettings() is private, so the test reaches it by reflection rather than widening the command's API for a test's sake.

php artisan test --filter=MqttConnectionSettingsTest → 4 passed.

dariuszprzepiora and others added 2 commits October 2, 2026 19:09
php-mqtt/client ConnectionSettings is immutable: every setter returns a
modified clone. buildSettings() discarded those return values, so the
username, password and TLS options never reached the connection. Brokers
with anonymous access disabled rejected the listener as "not authorised",
with nothing pointing at the missing credentials.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mqtt-listener was pinned to BROADCAST_CONNECTION=log. CollectionChanged is
ShouldBroadcastNow, so it is dispatched by the process that writes the row;
work order quantities updated from MQTT messages were logged instead of
sent to Reverb, and open pages only showed them after a manual reload.
Use reverb with the same REVERB_* settings as backend and queue-worker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • cla-signed

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: Mes-Open/OpenMes/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ebaf33c8-f2db-420c-bb01-421c6562d509

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…s rows

mqtt-listener was not the only service pinned away from Reverb. modbus-poller
and queue-worker set no BROADCAST_CONNECTION at all, so they fell back to the
config default — reverb — with no REVERB_HOST, REVERB_PORT or REVERB_SCHEME.
That resolves to https://:443, which goes nowhere.

The failure is invisible by design: CollectionBroadcaster::safeBroadcast()
reports the exception and swallows it, because a broadcast must never break the
write that triggered it. So the counter reading lands in the database and the
browser keeps showing the old number until someone reloads.

modbus-poller writes machine counter readings and queue-worker sends every
queued ShouldBroadcast event, so both need the same block the listener now has.
opcua-gateway is unaffected: it posts to the backend API, and the backend both
writes the row and broadcasts it.

Also adds a unit test for MqttListenCommand::buildSettings(). ConnectionSettings
is immutable, so a setter whose result is discarded is a no-op that nothing
reports; two of the four assertions fail without the credentials fix in this
branch.
@jakub-przepiora
jakub-przepiora merged commit a717b4f into Mes-Open:develop Oct 4, 2026
1 of 2 checks passed
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.

2 participants