Skip to content

SSE Real-time Event Push ​

Logged-in web pages receive one-way events via GET /api/events. SSE updates collection and protocol states; system health metrics are still read via the corresponding REST endpoints. A heartbeat only proves the connection received a message — it does not prove sensor collection, rule execution, or MQTT reporting is normal.

Events and Data ​

EventContent
sensor-dataPeriphExec collection results; updates pages per the actual data-source fields and quality
modbus-dataArray of Modbus data records; large arrays are sent in complete-record chunks
mqtt-statusMQTT status JSON
modbus-statusModbus status JSON
resyncRecovery hint when chunked push fails; re-fetch the current snapshot via REST
heartbeatText ping; sent when the maintenance period reaches 60 seconds

Business messages are JSON strings in event.data and must be JSON.parsed first. Named events use addEventListener; onmessage does not receive all named events. Sensor fields vary with configuration — top-level temperature and humidity are not guaranteed to exist. Invalid, expired, or failed reads must not be replaced with zero values; valid zeros can display normally.

The Modbus single-packet budget is 512 bytes. Oversized record arrays are sent on record boundaries; a single record cannot be chunked, and if the send queue fails, some records may already have been received — then request resync. Clients should read back snapshots to fill gaps; do not stitch these arrays into one raw JSON document, and do not rely on event IDs to replay all missed messages.

Connection and Memory Limits ​

The connection budget comes from ResourceProfile::TCP_SSE_BUDGET:

ChipSSE connection budget
ESP32, ESP32-C3, ESP32-C61
ESP32-S32

When the largest contiguous block of internally byte-addressable DRAM is below 8192 bytes, or memory protection is at SEVERE/CRITICAL, new connections return 503 and business broadcasts pause. When the budget is full, new connections are usually rejected; a concurrency race during connection setup still has a fallback that evicts old connections. Resource gating runs before the authentication check, so an unauthenticated request may also get 503 first; when resources allow but the session is invalid, 401 is returned.

The server updates connection activity with positive-length TCP ACKs and cleans up connections with no activity acknowledgment for 120 seconds; enqueueing a heartbeat does not refresh that time. Actual sends are also subject to the maintenance schedule — 60 seconds is not a strict timing or business-freshness guarantee. Memory and connection overhead includes TCP buffers, message queues, and client tracking; it cannot be estimated as a fixed 500 bytes.

Browser Integration ​

A same-origin EventSource('/api/events') automatically carries the device session cookie. The minimal example below waits 5 seconds before reconnecting after an error, reminds you to re-fetch the business snapshot on reconnect or resync, and cancels timers after leaving the page:

javascript
let source = null;
let retryTimer = null;
let stopped = false;

function recoverSnapshot(reason) {
  // The integrator serially reads the required REST data here,
  // verifying quality and sampling time.
  console.info('Business snapshot refresh needed:', reason);
}

function connectEvents() {
  if (stopped || source) return;
  const current = new EventSource('/api/events');
  source = current;
  current.addEventListener('sensor-data', event => {
    if (source !== current || stopped) return;
    try {
      const payload = JSON.parse(event.data);
      console.log('This collection:', payload);
    } catch (error) {
      recoverSnapshot('invalid_sensor_json');
    }
  });
  current.addEventListener('resync', () => recoverSnapshot('server_resync'));
  current.onopen = () => {
    if (source === current && !stopped) recoverSnapshot('connected');
  };
  current.onerror = () => {
    if (source !== current || stopped) return;
    current.close();
    source = null;
    if (retryTimer !== null) clearTimeout(retryTimer);
    retryTimer = setTimeout(() => {
      retryTimer = null;
      connectEvents();
    }, 5000);
  };
}

function stopEvents() {
  stopped = true;
  if (retryTimer !== null) clearTimeout(retryTimer);
  retryTimer = null;
  if (source) source.close();
  source = null;
}

window.addEventListener('pagehide', stopEvents);
connectEvents();

Re-establish the connection and refresh snapshots when the page resumes. The project's existing web client also provides exponential backoff and connection-stall detection; prefer reusing AppState event registration and page-leave cleanup when integrating.

Troubleshooting ​

SymptomCheck
401Log in again and confirm the same-origin session cookie
503 or connection closedClose extra tabs, watch internal DRAM and protection state, keep backoff
Only heartbeats, no business updatesCheck enabled collection rules, protocol tasks, and data validity
Data missing after reconnectRead back the business snapshot serially; heartbeats do not fill business data
Named events not receivedUse the corresponding addEventListener and parse event.data

Using SSE does not require running /api/mqtt/test first; that test endpoint stages the config, disconnects, and rebuilds MQTT. Use the status endpoints for ordinary reads and page recovery. Verify the current source boundaries against old device versions separately; endpoint connectivity does not mean physical or long-term-stability acceptance either.

Related: API Reference, Health Monitor, MQTT Configuration.

FastBee | fastbee.cn