Apps meet the physical world: sensors, actuators and IoT
Until now a Reactive app worked on data typed in, imported, or fetched from the open-data service. From today it can listen to the physical world: a temperature sensor publishing over MQTT, a Bluetooth scale, an ESP32 plugged into USB, an NFC tag, the phone’s accelerometer. And it can command it too: a switch in the document that turns on a real light. As always, everything runs in the browser: no server to install, no account, data stays on the device.
The principle: readings are data like any other
Every IoT directive pours its readings into the reactive store — a key for the latest value, a collection for the history — and from there everything Reactive already does applies: tables, aggregations, conditionals, Python charts, even the AI directives that summarize or answer questions about the data. A thermometer becomes a chart in three lines of text:
::mqtt-sub[temp]{topic="home/living/temp" into="readings"}
Now: :value[temp] °C — average :avg[readings]{field="value"}
:::python{data="readings"}
import matplotlib.pyplot as plt
plt.plot([float(r["value"]) for r in data["readings"]])
:::
MQTT: the universal channel
MQTT is the lingua franca of IoT — Home Assistant, Tasmota, ESPHome and virtually every microcontroller speak it. Three directives bring it into documents, in every browser:
::mqtt-subsubscribes a topic: each message updates a key and/or is appended to a history collection (JSON payloads become row fields).::mqtt-pubpublishes on click, with a fixed value or one taken from a field of the document.::mqtt-bindbinds a key to a topic both ways: a::togglein the document drives the actuator, and the state published by the device realigns the toggle.
The broker is declared once in the frontmatter (mqttServer: wss://...)
and only endpoints travel in the document — never credentials — the
same principle as sync keys.
Bluetooth, serial, NFC: the device in front of you
For nearby objects you don’t even need a broker:
::bleconnects to a Bluetooth LE device (battery, heart rate, environmental sensors…) and receives live notifications;::ble-writesends commands — an LED on an ESP32 turns on from a line of Markdown.::serialreads the USB port: the lines an Arduino prints withSerial.printlnare appended to a collection (JSON lines become fields),::serial-sendtalks back. The perfect bridge between an electronics classroom and a living spreadsheet.::nfcreads NFC tags with the phone: surveys, inventories, attendance by tapping the phone on a tag.::motionand::ambient-lightuse the phone’s sensors: accelerometer and ambient light, live in the store.
The browser acts as the guarantor: Bluetooth, serial and NFC start only from a user click, never automatically.
What about REST? ::iot-poll
For devices exposing an HTTP API (Shelly, Tasmota, or any JSON service)
there’s ::iot-poll: it polls the endpoint at regular intervals and keeps
the collection aligned with the current state. One practical caveat: the app
is served over HTTPS, so local-network http:// endpoints are blocked by
the browser — for local traffic the MQTT broker remains the main road.
What you can build
- Home dashboard: temperature and humidity over MQTT, toggles driving the lights, a Python chart of the daily trend — shared with the family through multi-user sync.
- Classroom lab: every desk with an ESP32 over USB, measurements appending into a table, live averages and charts; the document IS the lab report.
- Inventory with a phone: NFC tags on the shelves,
::nfcinto a collection,::ai-classifycategorizing the items. - Workshop monitoring:
::iot-pollon smart plugs, thresholds with:::show, an AI summary of the week’s consumption.
As with the rest of the platform, the editor helps: the new directives are in the autocomplete (with attributes and descriptions) and the AI assistant knows them and suggests them when you describe an app that talks to devices.
All the details are in the syntax guide and the tag reference.