Build a live MQTT sensor dashboard
Publish fictional readings through a public MQTT broker, validate the callbacks, and render the latest three values per sensor as a text dashboard.

Broker Bex: I want you to follow each reading from publish to callback, validation, history, and alert before trusting the dashboard.
Predict the reading, then compare it with the measurement.
Python 3 in your browser (JupyterLite)
Python · no installPublish fictional readings through a public MQTT broker, validate the callbacks, and render the latest three values per sensor as a text dashboard.
Open the notebook in your browser and run each Python cell; no install or account is needed.
Three ways to run: use JupyterLite here with no install; run main.py locally from the downloadable lab folder; or open the same notebook in Google Colab.
Steps
Step 1
- Do
- Run `python3 main.py --step 1` to connect a subscriber to the one-level sensor wildcard on a unique topic tree.
- You will see
- The terminal names `test.mosquitto.org:1883`, the run-specific publish root, the `/+` subscription, and the rule that `+` matches exactly one sensor name.
- Why it matters
- A bounded wildcard lets one dashboard consume several sensors without accidentally subscribing to the whole public broker.

Step 1 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 2
- Do
- Run `python3 main.py --step 2` to publish seven fictional readings and inspect them in the subscriber callback.
- You will see
- A seven-row table shows sensor, sequence, temperature, QoS, and observation time, followed by `Every row above came back through the public broker callback.`
- Why it matters
- The received callback rows prove a broker round trip; printing only the publisher's inputs would not prove subscription behavior.

Step 2 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 3
- Do
- Run `python3 main.py --step 3` to compare valid JSON readings with one deliberately malformed payload.
- You will see
- The validation ledger reports seven accepted messages and one rejected `freezer-b` payload, `not-json`, beside the required numeric-field rule.
- Why it matters
- Transport receipt and application acceptance are separate decisions; invalid payloads must not enter the dashboard history.

Step 3 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 4
- Do
- Run `python3 main.py --step 4` to render the latest accepted value for every sensor.
- You will see
- The dashboard table shows three sensors with latest temperature, sequence, timestamp, and state, followed by the total valid-message count.
- Why it matters
- Keeping ingestion history separate from presentation makes the callback small and lets the view be rebuilt from accepted state.

Step 4 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 5
- Do
- Run `python3 main.py --step 5` to display each sensor's last three accepted readings.
- You will see
- Three compact history rows show `seq:value`, while the explanation identifies a three-value FIFO window and the next eviction direction.
- Why it matters
- A fixed-length deque bounds memory while preserving enough recent context to see movement rather than one isolated number.

Step 5 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 6
- Do
- Run `python3 main.py --step 6` to evaluate role-specific alert thresholds against the latest values.
- You will see
- The table places each latest value beside either the freezer rule `value > -17.0` or the packing rule `value > 22.0`; all three decisions are ALERT.
- Why it matters
- A temperature number has meaning only with its sensor role and rule, so one universal threshold would be misleading.

Step 6 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 7
- Do
- Run `python3 main.py --step 7` to reconcile callback, QoS, validation, and shutdown counts.
- You will see
- The summary accounts for eight callbacks, their QoS levels, seven valid rows, one malformed row, and a clean disconnect, then warns that receipt does not approve a public broker for production commands.
- Why it matters
- Count reconciliation detects silent gaps, while the scope warning keeps a successful teaching round trip separate from production security approval.

Step 7 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
Chapter checks
These questions refer to the chapter’s examples. Use the return links to review their answers.
A developer prototypes a smart lock system using `test.mosquitto.org` and publishes lock/unlock commands to the topic `smartlock/device123/command`. They plan to ship this to customers. Which statement best explains why this is a critical security risk?
Return to the chapter’s knowledge checkA temperature sensor in a smart building should only be able to publish its own readings. Which ACL rule best enforces this least-privilege requirement?
Return to the chapter’s knowledge check
Return to MQTT Python: Reliable Client Patterns · Browse Labs