Skip to content

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., your practice guide

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 install

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

Tier 2 · Web · paste-in setup · No account

Version tested: Python 3.12.7 / Pyodide 0.27.6 in JupyterLite 0.6.4; Chromium 148.0.7778.96; captured against a local Mosquitto-compatible WebSocket broker because the capture host cannot reach the public broker; the notebook targets test.mosquitto.org; captureSource playwright:jupyterlite. Date: 2026-09-10.

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.

Open in your browser (new tab)

Steps

Screens captured against Python 3 in your browser (JupyterLite) Python 3.12.7 / Pyodide 0.27.6 in JupyterLite 0.6.4; Chromium 148.0.7778.96; captured against a local Mosquitto-compatible WebSocket broker because the capture host cannot reach the public broker; the notebook targets test.mosquitto.org; captureSource playwright:jupyterlite on 2026-09-10; the tool may have moved on — the text steps are the contract.

  1. 1 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 terminal capture. A blue ring marks the unique one-level wildcard subscription.
    Step 1 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  2. 2 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 terminal capture. A blue ring marks the statement that the displayed readings came through broker callbacks.
    Step 2 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  3. 3 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 terminal capture. A blue ring marks the rejected malformed payload.
    Step 3 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  4. 4 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 terminal capture. A blue ring marks the packing sensor's latest dashboard row.
    Step 4 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  5. 5 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 terminal capture. A blue ring marks the three retained freezer-a readings.
    Step 5 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  6. 6 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 terminal capture. A blue ring marks the explanation that thresholds depend on sensor role.
    Step 6 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
  7. 7 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 terminal capture. A blue ring marks the public-broker scope warning.
    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.

  1. 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 check
  2. A 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

Caution

The public broker is shared: publish only fictional data on the generated topic, never credentials, personal data, actuator commands, or production telemetry. Broker receipt does not prove durable storage or exactly-once application processing.

Return to MQTT Python: Reliable Client Patterns · Browse Labs