Platform
IoT
HTTP device ingest, shadow, rules, alarms and RPC on D1 — not MQTT.
IoT module
Devices, readings, shadow, rules, alarms and RPC live in D1 behind the Worker /iot/* surface. Devices POST JSON over HTTP — there is no native MQTT transport (see What you bring). Readings fan out as iot.reading on the device room, and room-scoped events (/rooms/:id/iot/events) become chat messages that ambient agents can react to.
Scaffold
npx @fluxy-chat/create-fluxy-chat@latest my-iot --example iot-panelKeep the app tab open, then:
curl -X POST "$WORKER/rooms/$ROOM/iot/events" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"deviceId":"sim-1","eventType":"telemetry","payload":{"temp":21}}'Register and ingest
Register a device with a member JWT. The Worker returns apiKey once (iot_…) — store it on the device.
curl -X POST "$WORKER/iot/devices" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"name":"probe","roomId":"YOUR_ROOM"}'
curl -X POST "$WORKER/iot/devices/DEVICE_ID/readings" \
-H "Authorization: Bearer $IOT_DEVICE_KEY" \
-H "Content-Type: application/json" \
-d '{"sensor":"temp","value":21,"unit":"C"}'Bind a room to get iot.reading on the chat timeline; without roomId the device uses a per-project room. Ingest also updates status, the reported shadow and evaluates threshold rules.
SDK
import { FluxyChatClient, createWorkerFluxyIoTClient } from "@fluxy-chat/sdk";
const client = new FluxyChatClient({ baseUrl, userId, token }); // member JWT
const iot = createWorkerFluxyIoTClient(client);
const { device, apiKey } = await iot.registerDevice({ name: "probe", roomId, type: "sensor" });
await iot.ingestReading(device.id, { sensor: "temp", value: 21, unit: "C" }, apiKey);
const shadow = await iot.setDesired(device.id, { setpoint: 21 }); // member sets desired
await iot.getShadow(device.id); // reported + desired
await iot.getHealth(device.id, "temp"); // rolling score: health, alerts (spike, trend)
await iot.createRule({
name: "hot",
deviceId: device.id,
condition: { sensor: "temp", operator: ">", value: 30 },
action: { type: "chat_message" }, // stored declarative metadata — see Rules & alarms
});
await iot.listAlarms(device.id); // then ackAlarm / clearAlarm / assignAlarm / unassignAlarm
await iot.sendRpc(device.id, "reboot", { delay: 5 }); // member queues
// device side (key auth): pullRpc(device.id, apiKey) → replyRpc(device.id, rpc.id, result, apiKey)Device auth
- Members (dashboard, SDK): project JWT.
- Devices: the
iot_*key, viaAuthorization: Bearer iot_…orX-Device-Key. A device key only works for its own device, and only on readings ingest, shadow read,reportedshadow patch, RPC pull and RPC reply. Queuing RPC and rules/admin calls are member-only (403 for device keys).
Realtime & agents
useChat({
roomId,
onServerEvent: (ev) => {
if (ev.name === "iot.reading") { /* ev.data: deviceId, sensor, value, unit, recordedAt */ }
},
});Two agent hooks: POST /rooms/:id/iot/events stores a room message (kind: "iot") that can trigger ambient agents, and when the project publish config sets iot_auto_agent_id every reading also invokes that agent with @<agentId> iot.reading sensor=value on the device room.
Rules & alarms
A rule is condition { sensor, operator, value } evaluated on every ingest for the whole project or one device: on a match the Worker opens an alarm (active). Lifecycle: ack (active → acked), clear (active/acked → cleared), assign/unassign. The action field is stored declarative metadata — it is not executed by the Worker today; bring your own dispatcher if you want webhooks or scene changes.
Limits
5,000 readings per project per UTC day; over that HTTP 429 quota_exceeded with retryAfterSeconds. A device lists as offline after 10 minutes without readings. List caps: readings 500, devices 100, rules/alarms/RPC 100.
Readiness
| Piece | Status |
|---|---|
| Devices, readings, shadow, health, rules → alarms, alarms lifecycle, RPC queue | Beta — Worker + D1, route and SDK tests |
iot-panel scaffold | Working example |
| MQTT / broker | Not included — bring your own |
| Rule action execution | Not included — action is declarative metadata |
What you bring
- An MQTT broker or gateway if your fleet speaks MQTT: devices (or your bridge) POST JSON to
/iot/devices/{deviceId}/readings. The Worker owns auth, quota, shadow, rules and history — not the device session. - A dispatcher for rule
actions if you want them to execute (the Worker raises the alarm and stores the intent).
Related: API reference — /iot/* is in the OpenAPI spec; dashboard /iot; JWT auth.