IUT Hackathon — Preliminary Round Submission
A real-time system that lets anyone monitor the electrical devices (lights and fans) of a 3-room office through two independent interfaces — a live web dashboard and a Discord bot — both backed by a single shared backend. Device state is simulated; no physical hardware is required for the demo.
- Project Overview
- Architecture
- Repository Structure
- Setup & Run
- Device Count — 15, not 18
- Assumptions & Design Decisions
- Diagrams
The office has three rooms — Drawing Room, Work Room 1, and Work Room 2 — each equipped with 2 fans and 3 lights (5 devices per room, 15 devices total). A backend simulator randomly flips device states every 8–15 seconds, driving a single in-memory store that acts as the authoritative source of truth. The React dashboard receives updates via Server-Sent Events (SSE) and re-renders without page refresh. The Discord bot queries the same REST API on demand and uses an LLM (Anthropic claude-haiku) to produce friendly, conversational responses. Both consumers always reflect the same live reality.
[Simulator: flips 1–3 devices every 8–15 s]
↓
[Device Store: single in-memory source of truth (15 devices)]
↓
[Express Backend API — port 3001]
│
├──SSE stream──► [React Dashboard — port 5173]
│ Live device panel · Power meter · Alerts · Floorplan
│
└──REST polls──► [Discord Bot]
!status · !room · !usage · Alert monitor (BONUS)
↓
[Anthropic LLM — claude-haiku-4-5]
Humanises raw JSON → friendly Discord messages
Both the dashboard and the bot read from the same backend. The bot never holds its own copy of device state, and neither does the dashboard — if they diverge, that is a bug.
IUT_HACKATHON/
├── backend/ # Phase 1 — Node.js + Express backend
│ ├── deviceStore.js # Single in-memory source of truth
│ ├── simulator.js # Randomly flips devices every 8–15 s
│ ├── sseManager.js # SSE client registry + broadcast
│ ├── alertEngine.js # After-hours & stuck-on alert logic
│ ├── usageCalculator.js# Live wattage + kWh accumulator
│ ├── routes/api.js # All REST + SSE endpoints
│ ├── server.js # Entry point
│ └── .env.example
│
├── dashboard/ # Phase 2 — React (Vite) dashboard
│ └── src/
│ ├── hooks/ # useDevices (SSE), usePoller (REST)
│ └── components/ # DevicePanel, RoomCard, DeviceTile,
│ # UsageMeter, AlertsPanel,
│ # ConnectionBanner, Floorplan (BONUS)
│
├── bot/ # Phase 3 — Discord bot (discord.js)
│ ├── bot.js # Entry point + command router
│ ├── commands/ # status.js, room.js, usage.js
│ └── services/ # backendClient.js, llmClient.js,
│ # alertMonitor.js (BONUS)
│
├── diagrams/ # Phase 4
│ ├── system_architecture.png # High-level system diagram
│ └── wiring_guide.md # ESP32 wiring plan for Wokwi/Tinkercad
│
└── README.md
All three services run independently. Start them in the order listed below.
- Node.js ≥ 18 (all three services)
- A Discord bot token (for the bot only) — create one at discord.com/developers
- An Anthropic API key (for LLM responses, optional — bot falls back to templates without it)
cd backend
cp .env.example .env # optional — defaults work without changes
npm install
npm start # or: npm run dev (uses nodemon)Environment variables (.env):
| Variable | Default | Description |
|---|---|---|
PORT |
3001 |
HTTP port |
OFFICE_HOURS_START |
9 |
Start of office hours (24h) |
OFFICE_HOURS_END |
17 |
End of office hours (24h) |
Endpoints (all on http://localhost:3001):
| Method | Path | Description |
|---|---|---|
| GET | /api/devices |
All 15 devices |
| GET | /api/rooms/:room |
5 devices for one room (drawing, work1, work2) |
| GET | /api/usage |
Total watts, per-room watts, estimated kWh |
| GET | /api/alerts |
Active after-hours and stuck-on alerts |
| GET | /api/events |
SSE stream — pushed on every simulator tick |
Requires the backend to be running first.
cd dashboard
npm install
npm run dev # Vite dev server on http://localhost:5173No environment variables needed. The Vite dev server proxies all /api requests to localhost:3001 automatically.
Open http://localhost:5173 in your browser. The dashboard will:
- Connect to the SSE stream immediately and show a live snapshot
- Update device states in real time without any page refresh
- Poll
/api/usageevery 5 seconds and/api/alertsevery 10 seconds - Show a red banner at the top if the backend becomes unreachable
- Offer a Floorplan tab (BONUS) with an animated SVG showing spinning fans and glowing lights
Requires the backend to be running. The bot does NOT require the dashboard.
cd bot
cp .env.example .env # fill in your tokens
npm install
npm startEnvironment variables (.env):
| Variable | Required | Description |
|---|---|---|
DISCORD_TOKEN |
✅ Yes | Bot token from Discord Developer Portal |
ANTHROPIC_API_KEY |
Enables LLM-powered responses; falls back to templates without it | |
BACKEND_URL |
defaults to http://localhost:3001 |
Backend base URL |
ALERT_CHANNEL_ID |
Channel ID for proactive alert posts (BONUS) |
Commands:
| Command | Description |
|---|---|
!status |
Summarises all 3 rooms' device states |
!room <name> |
Detail for one room (drawing, work1, work2) |
!usage |
Current total wattage + today's estimated kWh |
!help |
Lists available commands |
BONUS — Proactive alerts: If ALERT_CHANNEL_ID is set, the bot polls /api/alerts every 30 seconds and posts a message when a new alert appears. Each alert ID is tracked so the same alert is never posted twice per trigger cycle.
Note on Discord privileges: The bot requires the Message Content Intent to be enabled in the Discord Developer Portal (Bot → Privileged Gateway Intents → Message Content Intent ✅).
The original problem-statement PDF (page 2) mentions "18 devices" in two places but its own summary box on the same page states the correct count. The actual office layout is:
- 3 rooms × (2 fans + 3 lights) = 3 × 5 = 15 devices
A fan rated at 60 W and a light rated at 15 W gives a maximum possible draw of 165 W per room (2×60 + 3×15) and 495 W total across the office. This system uses 15 consistently throughout all code, API responses, and documentation.
| Area | Decision | Rationale |
|---|---|---|
| Office hours | 09:00–17:00, every day (no weekend distinction) | Spec says "9 AM–5 PM"; no weekday/weekend rule given — simplest correct interpretation |
| After-hours alerts | One alert per ON device (not one per room) | Finer granularity; the boss knows exactly which devices are on |
| Stuck-on condition | ALL 5 devices in a room must be ON for ≥ 2 h | Matches spec exactly; partial-room stuck-on is not flagged |
| kWh estimation | Σ (watts × elapsed_hours) accumulated since server start or midnight |
No database; accumulator resets on restart or at midnight. "Since server start or midnight — whichever is later" is noted in usageCalculator.js |
| Simulator interval | 8–15 seconds, randomised on each tick | Feels organic; avoids all-devices-changing-at-once visual noise |
| Simulator flip count | 1–3 devices per tick | Same reason; 1 makes the dashboard feel too slow, >3 too chaotic |
| SSE vs WebSockets | SSE (as spec requires) | One-way push, HTTP/1.1 compatible, native EventSource auto-reconnect |
| LLM model | claude-haiku-4-5 |
Fastest, cheapest Anthropic model; 256-token responses sufficient for 1-3 sentence summaries |
| Alert monitor poll | 30 seconds (bot-side) | Dashboard polls at 10 s; bot uses 30 s since it's a notifier, not a live UI |
| CORS | cors() with wildcard origin |
Development convenience; in production this would be locked to specific origins |
See diagrams/system_architecture.png
The schematic below represents the circuit wiring diagram for the Drawing Room (2 fans + 3 lights on an ESP32 with a 5-channel relay module and ACS712 current sensor), built in Wokwi:
Full wiring plan, pin-mapping table, and assembly steps can be found in diagrams/wiring_guide.md.

