Build stable windowed alerts from an MQTT stream
Publish a small sensor stream, expose a tumbling-window blind spot, and produce one stable deduplicated alert from the same evidence.

Data Dora: I want you to trace every alert back to its MQTT event IDs, window rule, and output guard.
Predict the reading, then compare it with the measurement.
Python 3 in your browser (JupyterLite)
Python · no installPublish a small sensor stream, expose a tumbling-window blind spot, and produce one stable deduplicated alert from the same evidence.
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` and inspect the event contract before processing any reading.
- You will see
- Under `Event contract`, the field table shows `source freezer-7`, `event_second reading time in seconds`, `celsius temperature in Celsius`, and `status valid`.
- Why it matters
- The table makes the source, clock, measurement unit, and status semantics inspectable before any processing decision uses them.

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` while connected to the internet; the program publishes six fictional readings to its random MQTT topic.
- You will see
- Under `MQTT messages received`, six `topic payload` lines show r1 through r6 arriving before `Delivery check: every published event ID returned to this subscriber.`
- Why it matters
- Seeing each broker-delivered topic and JSON payload lets the learner verify the source-to-intake handoff behind the final six-of-six count.

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 place all six event IDs into a four-entry recent-history buffer.
- You will see
- The `arrival buffer after arrival` table grows from `r1 r1` to `r6 r3 r4 r5 r6`, followed by the oldest-ID eviction rule.
- Why it matters
- The row-by-row state trace shows exactly when r1 and r2 leave capacity-four state, distinguishing intentional eviction from loss.

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 count the six-event burst in adjacent 60-second tumbling windows.
- You will see
- The window table places `r1@50 r2@52 r3@54` in `[0, 60)` and `r4@60 r5@62 r6@64` in `[60, 120)`, then says the 3-and-3 split hides the burst.
- Why it matters
- Showing boundaries and members proves why neither fixed window reaches six, rather than asking the summary count to explain itself.

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 evaluate a 60-second sliding window beginning at event-second 50.
- You will see
- The `[50, 110)` membership table marks every reading r1 through r6 `YES`, followed by `Rule: alert when the window contains at least 6 readings.`
- Why it matters
- The explicit boundary and six member rows let the learner recompute the alert and compare sliding-window behavior with the split tumbling windows.

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 pass counts 9, 11, 10, 9, 8, and 6 through separate on and clear thresholds.
- You will see
- The state trace reads `9 OFF`, `11 ON`, `10 ON`, `9 ON`, `8 ON`, and `6 OFF`, then names the turn-on and clear rules.
- Why it matters
- Following every input through both thresholds shows how hysteresis holds state through intermediate counts instead of merely reporting two transitions.

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 submit the same warm-run event ID three times to the output guard.
- You will see
- The decision table shows `warm-run-42 EMIT alert` once and `warm-run-42 SUPPRESS duplicate` twice before naming the one-per-ID rule.
- Why it matters
- Showing each candidate and guard decision makes the idempotent side effect auditable across repeated delivery.

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.
What makes an IoT streaming pipeline reviewable before downstream consumers depend on it?
Return to the chapter’s knowledge checkAfter a processor restart, replayed events produce duplicate alerts for the same equipment event. Which part of the pipeline review should be investigated first?
Return to the chapter’s knowledge check