Skip to content

Latest commit

 

History

History
102 lines (75 loc) · 4.05 KB

File metadata and controls

102 lines (75 loc) · 4.05 KB

Development

Purpose

Day-to-day workflow, environment, and the non-obvious pitfalls that have bitten this project. Read this before debugging anything that "should work".

Quick start

npm install
cp .env.example .env      # optional: LOG_LEVEL
npm run dev               # terminal 1 — Next.js dev server
npm run logs              # terminal 2 — pretty-printed tail of the log file

The dev server listens on http://localhost:3000 (Next.js auto-increments to 3001+ if 3000 is taken — watch the startup output for the real port). Configure the WLED host in the UI top bar.

Scripts

Script Does
npm run dev Next.js dev server
npm run build Production build
npm run start Production server
npm run logs tail -f logs/hex-hive.log | pino-pretty
npm run typecheck tsc --noEmit

Environment variables

Var Default Purpose
LOG_LEVEL debug Pino log level (trace/debug/info/warn/error)
NODE_ENV production disables stdout log stream

Logging

Pino writes JSON to logs/hex-hive.log (and stdout in dev). Categories: http, wled, ws, store, wave, pattern, mapper, sse. Tail with npm run logs for a colorized, human-readable view. The log is the primary debugging tool — every WLED call and user action is recorded.

Pitfall 1 — HMR and globalThis singletons

Next.js dev HMR reloads modules but does not reset globalThis. It can also load multiple copies of a module across webpack chunks. Long-lived singletons (wledWs, waveStreamer, sseBus, log, the config cache) therefore live on globalThis.

Two failure modes seen during development:

  • Stale-cache bug: a module-level let cache meant a route writing config and the WS subscriber reading config held different caches → "WS won't connect after host change". Fix: cache on globalThis.
  • Zombie-instance bug: waveStreamer was a plain export const new WaveStreamer(). After HMR, pattern/apply held the old instance and called stop() on it while a newer instance kept ticking → "I stopped the wave but POSTs keep firing". Fix: export a wrapper whose methods all route through globalThis.__hexhive_wave.

Rule of thumb: anything with a timer, a socket, or a cache must be reached through globalThis, never a captured module-level binding. When in doubt during dev, restart the dev server — it's the only guaranteed clean slate.

Pitfall 2 — pino transports don't bundle

Pino's worker-thread transports (pino/file, pino-pretty) don't survive Next.js webpack bundling. log.ts uses pino.destination / pino.multistream (synchronous, no worker threads) instead, and next.config.mjs lists pino in serverComponentsExternalPackages.

Pitfall 3 — WLED is a resource-constrained device

The target is an ESP8266. It has ~40KB of heap and a hard 16-segment limit.

  • The wave streamer POSTs at ~16Hz. Over long sessions this fragments WLED's heap. Healthy freeheap is 25KB+; under ~10KB the device starts dropping WebSocket clients and corrupting flash config. Reboot WLED to recover (POST /json/state {"rb":true} or power-cycle).
  • If colors look channel-swapped, check WLED's color order setting (GRB for WS281x) on the device's own settings page. This app never writes device config — a wrong color order is a device-side setting, often nudged by the heap corruption above.

Pitfall 4 — WLED transition is sticky

WLED remembers the last transition value across state updates. Live streaming must send transition: 0 or every frame fights a 1.4s interpolation. The encoder handles this for buildApplyHexColors.

Pitfall 5 — segment overlap

WLED's overlap rule: higher segment id wins. A per-LED i-array update on segment 0 is overridden by any active segments 1–15. Per-LED payloads must emit segments 1–15 as zero-length to win cleanly.

Stack

Next.js 14 (App Router) · React 18 · TypeScript (strict) · Tailwind CSS · Pino · ws · zod. See package.json for versions.