FluxyChat

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-panel

Keep 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, via Authorization: Bearer iot_… or X-Device-Key. A device key only works for its own device, and only on readings ingest, shadow read, reported shadow 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

PieceStatus
Devices, readings, shadow, health, rules → alarms, alarms lifecycle, RPC queueBeta — Worker + D1, route and SDK tests
iot-panel scaffoldWorking example
MQTT / brokerNot included — bring your own
Rule action executionNot 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.

On this page