raehDocs
Examples

JavaScript

Node and browser examples for ingest and subscribe.

Node: ingest synthetic PPG

Install:

npm install ws
// ingest-synth.mjs
import WebSocket from "ws";

const KEY   = process.env.RAEH_CLIENT_KEY;
const DM_ID = process.env.RAEH_DEVICE_MODEL_ID;

const url = new URL("wss://api.raeh.io/stream/ingest");
url.searchParams.set("api_key", KEY);
url.searchParams.set("device_id", `demo-${Math.random().toString(36).slice(2, 10)}`);
url.searchParams.set("device_model_id", DM_ID);

const ws = new WebSocket(url.toString());

function encodeFrame(slotId, t0Ms, samples) {
    const buf = new ArrayBuffer(12 + samples.length * 2);
    const view = new DataView(buf);
    view.setUint8(0, slotId);
    view.setBigInt64(1, BigInt(t0Ms), false);       // big-endian
    view.setUint16(9, samples.length, false);
    view.setUint8(11, 0);
    for (let i = 0; i < samples.length; i++) view.setInt16(12 + i * 2, samples[i], true); // samples are LE
    return Buffer.from(buf);
}

ws.once("message", async (buf) => {
    const { session_id, slots } = JSON.parse(buf.toString("utf8"));
    const slotId = slots[0].slot_id;
    console.log(`session: ${session_id}`);

    for (let i = 0; i < 60; i++) {
        const t0 = Date.now();
        const samples = Array.from({ length: 100 }, (_, k) =>
            Math.round(2048 + 500 * Math.sin(2 * Math.PI * 1.2 * k / 100))
        );
        ws.send(encodeFrame(slotId, t0, samples));
        process.stdout.write(`  frame ${i + 1}/60\r`);
        await new Promise((r) => setTimeout(r, 1000));
    }
    console.log();
    ws.close();
});
RAEH_CLIENT_KEY=raeh_... RAEH_DEVICE_MODEL_ID=<uuid> node ingest-synth.mjs

Node: subscribe

// subscribe.mjs
import WebSocket from "ws";

const KEY = process.env.RAEH_CLIENT_KEY;
const ws = new WebSocket(`wss://api.raeh.io/stream/subscribe?api_key=${encodeURIComponent(KEY)}`);

let acked = false;
ws.on("message", (buf) => {
    const msg = JSON.parse(buf.toString("utf8"));
    if (!acked) { acked = true; return; }   // drop subscribe ack
    if (msg.type) console.log(`${msg.type.padEnd(5)} ${String(msg.value).padStart(6)} ${msg.unit ?? ""}`);
});
RAEH_CLIENT_KEY=raeh_... node subscribe.mjs

Browser: subscribe-only UI

Copy-paste into any HTML file and open in a browser. Great for prototyping a live-dashboard view.

<!doctype html>
<html>
<head><meta charset="utf-8"><title>Raeh Live</title></head>
<body>
  <h1>Raeh Live</h1>
  <input id="key" placeholder="raeh_..." style="width:320px">
  <button id="connect">Connect</button>
  <div>HR: <span id="hr">–</span> bpm · SpO₂: <span id="spo2">–</span> % · RR: <span id="rr">–</span> brpm</div>
  <pre id="log" style="height:200px;overflow:auto;background:#111;color:#0f0;padding:8px"></pre>

<script>
  const $ = (id) => document.getElementById(id);
  const log = (m) => $("log").textContent += m + "\n";

  $("connect").onclick = () => {
    const key = $("key").value.trim();
    if (!key) return;
    const ws = new WebSocket(`wss://api.raeh.io/stream/subscribe?api_key=${encodeURIComponent(key)}`);
    ws.onopen    = () => log("connected");
    ws.onclose   = (e) => log(`closed ${e.code}`);
    ws.onmessage = (ev) => {
      const msg = JSON.parse(ev.data);
      if (!msg.type) return;
      const el = $(msg.type);
      if (el) el.textContent = typeof msg.value === "number" ? msg.value.toFixed(msg.type === "hr" ? 0 : 1) : msg.value;
      log(`${msg.type}: ${msg.value} ${msg.unit ?? ""}`);
    };
  };
</script>
</body>
</html>

Browser: ingest (rarely needed)

Browsers can open /stream/ingest too (same URL.searchParams pattern), but for production wearables the ingest side runs in firmware or a native app, not the browser. Use browser ingest only for prototyping from a USB-connected sensor via Web Bluetooth or Web Serial.

On this page