WebSocket Protocol
The DEJA.js server provides a WebSocket interface for real-time bidirectional communication between browser apps and the backend. All messages are JSON objects with an action field and a payload field. The default port is 8082, configurable via the VITE_WS_PORT environment variable.
Connection
Connect to the WebSocket server at the configured host and port:
ws://localhost:8082
In production or when served over HTTPS, clients should use wss:// instead. The Monitor app resolves the protocol automatically based on window.location.protocol.
Connection Lifecycle
- Client connects -- The server adds the client to its general connections pool.
- Server sends
ack-- Immediately after connection, the server sends an acknowledgment with the layout ID and server ID. - Server sends
wsconnected-- The server sends the client's IP address and server ID. - Client sends/receives messages -- Normal bidirectional communication.
- Client subscribes to devices (optional) -- The client can subscribe to device-specific serial monitoring.
- Client disconnects -- The server removes the client from all connection pools and device subscriptions.
Message Format
Every message is a JSON object with two fields:
{
"action": "string",
"payload": {}
}
The action field identifies the message type. The payload field contains action-specific data and its structure varies by action.
Server-to-Client Actions
These messages are broadcast from the server to all connected clients.
ack -- Connection Acknowledgment
Sent immediately when a client connects.
{
"action": "ack",
"payload": {
"layoutId": "tamarack",
"serverId": "DEJA.js"
}
}
wsconnected -- Client Connected
Sent after the ack message with client identification.
{
"action": "wsconnected",
"payload": {
"ip": "192.168.1.42",
"serverId": "DEJA.js"
}
}
dcc -- DCC Command Broadcast
Broadcast whenever a DCC-EX command is sent to the serial port. Allows clients to display a live command log.
{
"action": "dcc",
"payload": "t 3 50 1"
}
connected -- Serial Port Connected
Broadcast when a serial connection to a DCC-EX CommandStation is established.
{
"action": "connected",
"payload": {
"baudRate": 115200,
"device": "CommandStation",
"path": "/dev/tty.usbmodem1101"
}
}
portList -- Available Serial Ports
Broadcast in response to a listPorts command.
{
"action": "portList",
"payload": [
"/dev/tty.usbmodem1101",
"/dev/tty.usbserial-110"
]
}
status -- Server Status
Broadcast in response to a getStatus, status, or ping command.
{
"action": "status",
"payload": {
"client": "dejaJS",
"isConnected": true
}
}
broadcast -- General Broadcast
Wraps miscellaneous messages from serial connection handlers.
{
"action": "broadcast",
"payload": {}
}
Device Serial Monitoring
The WebSocket server supports device-specific serial monitoring through a subscribe/unsubscribe protocol. This allows the Monitor app to receive filtered serial I/O for individual hardware devices without getting traffic from other devices.
Client-to-Server: Subscribe to Device
{
"action": "subscribe-device",
"deviceId": "tj-eagle-nest-pico"
}
Server response:
{
"action": "device-subscribed",
"payload": {
"deviceId": "tj-eagle-nest-pico",
"success": true
}
}
Client-to-Server: Unsubscribe from Device
{
"action": "unsubscribe-device",
"deviceId": "tj-eagle-nest-pico"
}
Server response:
{
"action": "device-unsubscribed",
"payload": {
"deviceId": "tj-eagle-nest-pico",
"success": true
}
}
Server-to-Client: Serial Data
Sent only to clients subscribed to the specific device.
Incoming serial data (from device to server):
{
"action": "serial-data",
"payload": {
"deviceId": "tj-eagle-nest-pico",
"data": "<p1>",
"timestamp": "2025-01-15T14:30:22.123Z",
"direction": "incoming"
}
}
Outgoing serial commands (from server to device):
{
"action": "serial-data",
"payload": {
"deviceId": "tj-eagle-nest-pico",
"data": "T 1 1",
"timestamp": "2025-01-15T14:30:22.456Z",
"direction": "outgoing"
}
}
Connection Management
General Connections
All connected clients receive broadcast messages (DCC commands, status updates, port lists). The server maintains an array of active WebSocket connections and iterates over them for each broadcast, skipping any that are not in the OPEN state.
Device Connections
Device-specific connections are tracked in a separate Map<string, WebSocket[]> keyed by device ID. A single client can subscribe to multiple devices, and multiple clients can subscribe to the same device. When a client disconnects, the server removes it from all device subscription lists.
Cleanup
On server shutdown (SIGINT or SIGTERM):
- All general connections are closed with code
1000(normal closure). - All device-specific connections are closed.
- The connection maps are cleared.
- The WebSocket server is closed.
Example: Monitoring a Device
const ws = new WebSocket('ws://localhost:8082')
ws.onopen = () => {
// Subscribe to a specific device
ws.send(JSON.stringify({
action: 'subscribe-device',
deviceId: 'tj-thunder-city-pico'
}))
}
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
switch (message.action) {
case 'device-subscribed':
console.log('Subscribed to', message.payload.deviceId)
break
case 'serial-data':
const { deviceId, data, direction, timestamp } = message.payload
console.log(`[${direction}] ${deviceId}: ${data}`)
break
case 'dcc':
// General DCC command broadcast
console.log('DCC:', message.payload)
break
default:
// Other broadcast messages
break
}
}