Events
Every controller on v2 firmware keeps an audit log of what it did and why: outputs switched and by whom, schedule changes, commands received, reboots, power failures, link changes, dosing sessions and faults. Records are uploaded to the cloud in signed batches, stored once, and acknowledged so the controller can discard them.
List events
Section titled “List events”GET/api-gateway/v2/iot-shadow/device/{deviceId}/events
| Parameter | Default | Meaning |
|---|---|---|
limit |
100 | 1 to 500 |
before_seq |
Return only events with seq below this value, for paging backwards |
|
type |
Filter on one event type, 1 to 14 |
{ "status": "SUCCESS", "data": { "events": [ { "seq": 20086, "ts": 1758400000, "type": 2, "type_name": "AUX", "a": 5, "b": 1, "c": 178, "d": 0, "raw": "764e0000000ecf68020501b200000000", "detail": { "slot": 5, "on": true, "trump": false, "source": 178 }, "received_at": "2026-09-21T00:30:12Z" } ], "cursor": { "device_id": "02_53_4D_00_00_01", "epoch": 0, "max_seq": 20086, "acked_seq": 20086, "updated_at": "2026-09-21T00:30:12Z" } }}Events are ordered newest first. seq is the controller’s own 1-based sequence number and ts the controller’s UTC clock when the event was logged. received_at and cursor.updated_at are RFC 3339 timestamps in the server’s time zone. a, b, c and d are the raw fields; detail is their decoded meaning. A controller that has never uploaded returns [] and a null cursor.
Event types
Section titled “Event types”type |
type_name |
detail |
Meaning |
|---|---|---|---|
| 1 | BOOT |
reset_reason, fw_version |
Controller started. Reset reasons: 1 power on, 2 external reset, 3 software reset, 4 panic, 5 to 7 watchdog, 8 deep sleep wake, 9 brownout |
| 2 | AUX |
slot, on, trump, source |
An output was commanded. source says by whom, see below |
| 3 | CMD |
cmd_code, transport |
A v1 command that changes something was received |
| 4 | SCHED |
op, index, transport |
Schedule created, updated, deleted, enabled or disabled |
| 5 | TIME_SET |
old_utc, new_utc |
Clock adjusted by more than 2 seconds |
| 6 | POWER_FAIL |
power_off, power_on |
Power was lost and restored |
| 7 | VERIFY_FAIL |
path, transport |
A signed frame failed verification. path: 1 action, 2 query, 3 local bridge |
| 8 | ERROR_BITS |
error_mask |
The controller’s error mask changed |
| 9 | LINK |
link, up |
Wi-Fi (1) or Ethernet (2) link went up or down |
| 10 | ALERT_BITS |
alert_mask |
The controller’s alert mask changed |
| 11 | SESSION |
kind, start, duration_sec |
Start or end of a backwash, flow calibration, pump run or dosing session. Kinds: 1 backwash, 2 flow calibration, 3 pump run, 4 pH doser, 5 mineral, 6 salt, 7 liquid |
| 12 | AUX_RESULT |
slot, on |
An output actually changed state |
| 13 | DROPPED |
manual_reset, dropped |
Records were discarded because the log was full or reset |
| 14 | FAULT |
kind, value, extra |
1 dry run, 2 flow, 3 output mismatch |
transport names the channel a command arrived on: v1-wifi, v1-eth, v1-ap, v2-wifi, v2-eth, v2-ap, v1-lan, v2-lan.
source on AUX events:
| Value | Origin |
|---|---|
| 47 | Front panel |
| 14 | Schedule |
| 28 | v1 command, over any transport, including the cmd pass-through |
| 178 | v2 aux action |
| 107 | Wireless button |
| 10 | Dosing logic |
| 161 to 165 | Pump, heater and solar logic |
| 255 | Pump logic |
| 31 | Spa logic |
| 26 | Solar logic |
| 11, 151 to 153 | Flow protection |
| 0 | Restored at boot |
Delivery guarantees
Section titled “Delivery guarantees”- The cloud stores each record once, keyed on the controller, an epoch and
seq. Re-uploads are ignored. - The cloud acknowledges the highest contiguous
seqit holds. If a batch arrives with a gap, the acknowledgement repeats the previous value, so the controller’s cursor does not advance and it resends. - If a controller’s log is reset and
seqrestarts, the cloud opens a new epoch; the listing shows the current epoch.
Rewind
Section titled “Rewind”POST/api-gateway/v2/iot-shadow/device/{deviceId}/events/rewind
{ "from_seq": 0 }Asks the controller to re-upload from from_seq onward (0 means the oldest record it still holds). Useful after a cloud-side gap. The response is an action result, { "action": "evt_rewind", "counter", "confirmed" }. Replayed records that already exist are ignored, so the cursor never moves backwards.

