A complete Node.js web app for controlling Smart-Bus G4 home automation devices via a SmartGate gateway. Includes a premium dark-themed web UI for managing lights, appliances, scenes, and schedules — all without writing code.
Key finding for developers: If your packets reach the SmartGate but dimmers never respond, the answer is almost certainly the device type field. Read the Protocol section below.
The web app runs on your local network and is accessible from any phone, tablet, or browser on the same WiFi.
- Rooms — control lights and appliances per room with on/off toggle
- Scenes — activate multiple devices at once with one tap
- Schedules — time-based automation with sunrise/sunset support
- Settings — manage devices, rooms, and system config without touching code
This project was built by reverse-engineering what the official iOS Smart-Bus app actually sends, using tcpdump. The findings below took significant debugging to arrive at and are not clearly documented anywhere online.
Every UDP packet sent to the SmartGate must follow this exact format:
[4 bytes] SmartGate IP as raw bytes e.g. 0xC0 0xA8 0x56 0xA6 for 192.168.86.166
[10 bytes] ASCII string "SMARTCLOUD"
[2 bytes] 0xAA 0xAA
--- HDL content ---
[1 byte] Length (counts from this byte through the last CRC byte)
[1 byte] Sender subnet
[1 byte] Sender device
[2 bytes] Device type ← THE CRITICAL FIELD (see below)
[2 bytes] Opcode 0x00 0x31 for single-channel control
[1 byte] Target subnet
[1 byte] Target device
[1 byte] Channel (1-based)
[1 byte] Level (0–100)
[2 bytes] Additional delay 0x00 0x00 for immediate
[2 bytes] CRC16-XMODEM
For a dimmer control command the HDL content is 15 bytes, making the full UDP payload 31 bytes.
This is the #1 reason Smart-Bus implementations fail silently.
| Value | Description | Works? |
|---|---|---|
0x0119 |
iOS Smart-Bus app device type | ✅ YES |
0x01BC |
Often suggested in docs (444) | ❌ Silently dropped |
0xFFFE |
Generic PC type | ❌ Silently dropped |
The SmartGate or the dimmer modules silently discard commands from unrecognised device types — no error, no response. Using 0x0119 (what the official iOS app uses) makes dimmers respond immediately.
How we found this: Running tcpdump while the iOS app controlled a light. The SmartGate rebroadcasts all traffic as UDP broadcast on port 6000, so you can capture the exact bytes the app sends:
sudo tcpdump -i en0 host YOUR_SMARTGATE_IP -X -s 0Do not use the SmartGate's own S-BUS address as the sender. The SmartGate is typically at subnet 1, device 100 (1.100). If you send as 1.100, the SmartGate appears to discard the command to prevent loopback. Use any other address — this project uses 1.50.
Computed over all HDL bytes from the length byte through the last content byte (not including the 2 CRC bytes themselves). Init = 0x0000, poly = 0x1021, no input/output bit reflection.
function crc16(buf) {
let crc = 0x0000;
for (let i = 0; i < buf.length; i++) {
crc ^= buf[i] << 8;
for (let j = 0; j < 8; j++)
crc = (crc & 0x8000)
? (((crc << 1) ^ 0x1021) & 0xFFFF)
: ((crc << 1) & 0xFFFF);
}
return crc;
}| Opcode | Direction | Description |
|---|---|---|
0x0031 |
Controller → Dimmer | Single channel control |
0x0032 |
Dimmer → Broadcast | Dimmer status / acknowledgement |
If you send 0x0031 and never see 0x0032 in tcpdump, the device type or sender address is wrong.
The protocol is identical for both. The physical difference:
- True dimmers: level 0–100 smoothly controls brightness
- Relay modules: any level > 0 turns ON (reports back as 100), level 0 turns OFF
- Node.js v16+ (tested on v18)
- A Smart-Bus G4 SmartGate connected to your local network
- Your Smart-Bus devices on the same network subnet
git clone https://github.com/YOUR_USERNAME/smartbus-controller.git
cd smartbus-controllerNo npm install needed — uses only Node.js built-in modules (http, dgram, fs, path, crypto).
Edit the top of server.js:
let SMARTGATE_IP = '192.168.1.100'; // Your SmartGate IP
let SMARTGATE_PORT = 6000; // Default SmartGate UDP port
const SERVER_PORT = 3001; // Web UI portOr configure it later through the web UI under Settings → System.
node server.jsThen open http://YOUR_NAS_IP:3001 in any browser on the same network.
In DSM → Control Panel → Task Scheduler → Create → Triggered Task → Boot-up:
#!/bin/sh
NODE="/volume1/@appstore/Node.js_v18/usr/local/bin/node"
DIR="/volume1/homes/YOUR_USER/smartbus-controller"
pkill -f "smartbus-controller/server.js" 2>/dev/null
sleep 1
cd "$DIR"
nohup "$NODE" server.js > "$DIR/server.log" 2>&1 &Add rooms and assign devices (lights, water heaters, AC units, etc.) to them. Each device is a channel on a Smart-Bus dimmer/relay module, identified by subnet, device number, and channel.
Create scenes that control multiple devices at once. Tap to activate. Useful for "Movie Night" (dim lights), "Good Morning" (turn on specific devices), etc.
Automate scenes or devices on a time schedule. Supports:
- Specific time (e.g. 07:00)
- Sunrise / sunset with ± minute offset
- Day-of-week selection (weekdays, weekends, or specific days)
- Add/edit/delete rooms, S-BUS devices, and channels
- Set SmartGate IP
- Set your location (latitude/longitude) for sunrise/sunset calculations
All configuration is stored as plain JSON files in ./data/:
data/
rooms.json ← rooms and their devices/channels
devices.json ← S-BUS device addresses
scenes.json ← scenes and their actions
schedules.json ← schedules
settings.json ← system settings
Back this directory up to preserve your configuration.
The server exposes a REST API used by the web UI:
POST /api/light { subnet, device, channel, level }
GET /api/rooms list rooms
POST /api/rooms create room
PUT /api/rooms/:id update room
DEL /api/rooms/:id delete room
GET /api/scenes list scenes
POST /api/scenes create scene
POST /api/scenes/:id/activate activate scene
GET /api/schedules list schedules
POST /api/schedules create schedule
GET /api/settings get settings
PUT /api/settings update settings
GET /api/state current light state (in-memory)
GET /api/health health check
The Smart-Bus protocol has minimal public documentation. The working implementation here was arrived at by:
- Capturing traffic with
tcpdumpwhile the official iOS Smart-Bus app controlled lights - Decoding each packet byte-by-byte
- Identifying that the device type field (
0x0119) was the critical difference between the iOS app's working packets and our non-working packets - Verifying with CRC checks that our packet construction was otherwise correct
The SmartGate's broadcast behaviour (rebroadcasting all S-BUS traffic as UDP on the LAN) makes this kind of reverse-engineering straightforward — you don't need physical bus access.
If you find this useful or have corrections to the protocol documentation, pull requests and issues are welcome.
- Smart-Bus G4 SmartGate (HDLMGWSN-40)
- HDL dimmer modules (type
0x01BCin their own responses) - Relay modules
- Synology NAS (DS series) running Node.js v18
MIT