Talk to your HDL Buspro smart home. "Hey Google, activate Screens up", and the screens go up.
Busbridge is a small, self-hosted bridge between your HDL Buspro bus and everything else. Control your house from its web pages, from Google Assistant through IFTTT, or from anything else that can send a web request. It runs happily on a Raspberry Pi.
π± To control your HDL devices from your phone, there's also Smart Home for HDL Buspro, an app for Android.
What you get Β· How it works Β· Quick start Β· Raspberry Pi Β· Voice commands Β· Updating Β· Security
- π Finds your devices. Searches the bus and lists what answers: dimmers, relays, curtains, panels, sensors, and 230+ other types.
- ποΈ Actions without code. Set up scenes, dimmers, curtains, panel buttons and switches on a web page.
βΆοΈ Run them right there. Test an action with one click, or set a dimmer to any level.- π€ Voice control. Google Assistant through IFTTT, or Siri Shortcuts, Tasker, Home Assistant,
curlβ¦ - π Live activity. The latest 100 actions run or refused, with when and from where, as they happen.
- π Live status. Is the HDL bus alive, how busy is it, and how warm is the Pi.
- π± Made for your phone too. Light or dark, like your phone, and on your home screen like an app.
- π Protected. A random key guards the API. The pages only answer your home network, or Tailscale when you're away.
- π Safe updates. One command checks that the new version runs on your Java before installing it, then restarts the server.
![]() |
![]() |
| The devices on the bus | Recent actions and refusals |
![]() |
![]() |
| Your actions, one click away | On your phone, in the dark |
Screenshots show example data.
π¬ See it in action: the original demo on YouTube
flowchart LR
A["π£οΈ Hey Googleβ¦"] --> B[IFTTT]
B -- "web request + key" --> C["π This server"]
C -- "HDL commands (UDP)" --> D[HDL IP gateway]
D --> E["π‘ Lights, curtains, panelsβ¦"]
You set up actions on the server's web page. Each action gets its own URL. When a request with the right key arrives at that URL, the server sends the action's HDL commands to the bus.
You need:
- An HDL Buspro IP gateway on the same network
- Java 17 or later (e.g. Eclipse Temurin)
- The server JAR
java -cp hdlserver-LATEST.jar com.roysolberg.java.hdlserver.ApplicationThen open http://<the computer's IP address>:4567 from a computer on the same network. π
Logs go to the logs folder, next to hdlserver.db (your key, actions and devices). On Windows, use javaw instead of
java to run it without a window.
For a server that starts by itself and keeps running, set it up as a service, like below.
1. Java and Git. Raspberry Pi OS 11 (Bullseye) or later can run Java 17. Check with java -version, and if it's older
than 17:
sudo apt update
sudo apt install openjdk-17-jre-headless git(On Raspberry Pi OS 13, default-jre-headless gives Java 21, which works too.)
2. Get the server.
cd ~
git clone https://github.com/roys/java-hdl-webserver.git3. Try it. It should start and find your devices within a few seconds. Stop it with Ctrl + C.
cd ~/java-hdl-webserver/releases
java -cp hdlserver-LATEST.jar com.roysolberg.java.hdlserver.Application4. Run it as a service, so it starts with the Pi and restarts if it ever stops. Paste this as it is (it fills in your user name and home folder):
sudo tee /etc/systemd/system/hdlserver.service > /dev/null <<EOF
[Unit]
Description=Busbridge for HDL Buspro
Wants=network-online.target
After=network-online.target
[Service]
User=$USER
WorkingDirectory=$HOME/java-hdl-webserver/releases
ExecStart=/usr/bin/java -cp hdlserver-LATEST.jar com.roysolberg.java.hdlserver.Application
Restart=on-failure
RestartSec=10
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=$HOME/java-hdl-webserver/releases
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload && sudo systemctl enable --now hdlserverCheck it with systemctl status hdlserver.
Coming from an older guide? It started the server with
@reboot ~/java-hdl-webserver/launcher.shin the crontab. Remove that line when you switch to the service, so the server isn't started twice.
5. Open it to the Internet (only needed for IFTTT and other outside callers):
- Give the Pi a fixed IP address in your router.
- Expose only
/api/, preferably through an HTTPS reverse proxy. If you forward a port directly instead, forward a high port number other than 4567 to port 4567 on the Pi. - Get a host name that follows your IP address, with dynamic DNS (many routers can do this themselves).
Only the API needs to be reachable from the Internet. For anything else, like SSH or the web pages, use a VPN like Tailscale instead of forwarding more ports.
6. Done! β
Open http://<the Pi's IP address>:4567 and set up your first action.
π Reading the logs
Inside ~/java-hdl-webserver/releases:
tail -f logs/hdl-server.log # what the server does
tail -f logs/hdl-bus.log # everything on the HDL busA new log file is started every day, and files older than 30 days are deleted. The service's own output, including any
errors at startup, is in journalctl -u hdlserver.
Google Assistant doesn't talk to the server directly, so IFTTT sits in between. You'll need:
- IFTTT Pro, since IFTTT's Webhooks need a paid plan.
- The server reachable from the Internet (step 5 above).
On the server's Actions page, create a custom action. Give it a name, e.g. Movie night, and pick what it should do. The form names each value the operation needs:
- A scene is often the easiest: choose the device and Scene control, then the Area and Scene number (HDL's setup tool shows them).
- A panel button works great too: choose the panel and Panel control, then Function
18(control button), the Button number, and1(on) or0(off).
Add more commands to do several things at once.
Click βΆ Run to try it. π―
-
Go to ifttt.com/create.
-
If This: choose Google Assistant and Activate scene. Name the scene, e.g. Open the garage.
-
Then That: choose Webhooks and Make a web request, and fill it in like this. The action's URL and your key are on the Actions page.
Field Value URL http://<your host name>:<your port>/api/actions/<action number>Method POSTContent type application/jsonBody {"auth":"<your key>"} -
Save, wait a minute, and say: "Hey Google, activate Open the garage." π
You need one action and one applet for each command.
Dimmer actions take a level from 0 to 100, so one action covers every level: "β¦set the kitchen to 30 %". IFTTT's Google Assistant can't pass on a number any more, so for voice, create a custom action for each level you want. You can still use dimmer actions with any level from the Actions page, or from anything that can send a web request:
curl -X POST -H 'Content-Type: application/json' -d '{"auth":"<your key>","value":30}' \
http://<your host name>:<your port>/api/actions/dimmer/kitchenπ‘ Google is replacing Google Assistant with Gemini on its Home devices. If your phrases stop working, check that IFTTT's Google Assistant service still works with yours.
At home, Smart Home for HDL Buspro talks to HDL Buspro directly. Away from home it can't, since HDL Buspro's broadcasts don't cross a VPN. Paired with Busbridge, the app reaches the bus through it instead, over your VPN, e.g. Tailscale on the Pi and on the phone. This needs version 1.4 or later of the app.
- On Busbridge's Settings page, tick Let the Android app connect and save. That opens port 4568, with HTTPS, for your home network and VPN only, and shows a QR code.
- At home, open Busbridge in the app and scan the code. On the phone itself, tap Open in the app instead. The code works once, for 5 minutes; Connect a phone makes a new one.
- That's it. Away from home, the app uses Busbridge by itself.
Settings lists the paired phones. Remove one you've lost, and it can't connect again.
The app can't reach Busbridge? Check that the firewall on Busbridge's computer lets in TCP port 4568 from your home network and VPN:
| Firewall | Command |
|---|---|
| ufw (Raspberry Pi OS, Ubuntu) | sudo ufw allow from 192.0.2.0/24 to any port 4568 proto tcp and sudo ufw allow in on tailscale0 to any port 4568 proto tcp, with your home network's addresses instead of 192.0.2.0/24, e.g. 192.168.0.0/24 |
| firewalld (Fedora, RHEL) | sudo firewall-cmd --permanent --add-port=4568/tcp && sudo firewall-cmd --reload |
| macOS | Allow Java when macOS asks, or in System Settings β Network β Firewall β Options |
Another port than 4568: start the server with -Dhdlserver.appPort=<port>.
Run this inside ~/java-hdl-webserver:
./update.shIt checks that the new version runs on your Java before it changes anything, and then restarts the service. To be told
when there's a new version, turn on Check for updates in Settings, and the Status page will say so. Your key,
actions and devices are kept (they're in hdlserver.db), so your IFTTT applets keep working.
Didn't use git clone? Replace hdlserver-LATEST.jar with
the latest one, and restart the server.
βοΈ Settings: another port, or a host name with dots in it
Settings go before -cp in the start command (or in ExecStart in the service, followed by sudo systemctl daemon-reload
and a restart):
| Setting | What it does |
|---|---|
-Dhdlserver.port=8080 |
Listens on another port than 4567. Remember the port forwarding in your router, and your IFTTT applets' URLs. |
-Dhdlserver.allowedHosts=hdl.example.com |
Lets you open the pages by a host name with dots in it. IP addresses and local names like raspberrypi or raspberrypi.local always work. |
-Dhdlserver.appPort=4600 |
Lets the Android app connect on another port than 4568. |
-Dhdlserver.busPort=6001 |
Uses another UDP port than 6000, where HDL devices are, so nothing reaches the bus. For testing. |
java -Dhdlserver.port=8080 -Dhdlserver.allowedHosts=hdl.example.com -cp hdlserver-LATEST.jar com.roysolberg.java.hdlserver.ApplicationVersion 2 needs Java 17 or later (version 1 ran on Java 8). Everything else stays the same: the start command, port
4567, the API your IFTTT applets call, and hdlserver.db with your key, actions and devices.
Version 1 lives on in the master branch, so git pull keeps you on version 1 until you choose to switch.
- Check your Java with
java -version. If it's older than 17, install a newer one first (see the Raspberry Pi setup). - Switch to version 2 inside
~/java-hdl-webserver:(Or, without Git: replace the JAR with the latest one.)git fetch origin git checkout v2
- Restart the server. From now on,
./update.shkeeps you up to date.
Going back to version 1.1.1, which runs on Java 8, is just git checkout master and a restart (or download it from
the 1.1.1 release). If the server is started on a Java
older than 17, it says why, on the screen and in logs/hdl-server.log, and doesn't start.
Using the service from step 4? It's new in version 2: set it up, and remove the old
@reboot line from the crontab.
Small differences from version 1, if you call the server in other ways than described here
- The API no longer requires an
Accept: application/jsonheader (version 1 answered 404 to other values). - The web pages' forms only read their fields from the request body, not from the URL.
- Requests with an
X-HTTP-Method-Overrideheader are refused (400), and so are ambiguous paths like/api/%2e%2e/settings.
The one thing to know first: HDL Buspro has no security of its own. Anyone on the network HDL's IP module is on can switch, read and reprogram every device on the bus, with or without Busbridge, since HDL doesn't check who is asking and sends everything in plain text. Busbridge can't change that while the IP module is on your ordinary home network. So today, your home network is the lock.
What Busbridge protects:
- π The API needs a key, a random one made the first time the server starts. Requests without it are refused, and it only runs the actions you set up.
- π The web pages only answer your local network or your Tailscale network. Other web sites can't make your browser change anything on them.
- π± The Android app's port is off until you turn it on, uses HTTPS, and only answers your local network or VPN. The app trusts only Busbridge's own certificate, which it gets from the QR code it's paired with, so no one in between can pass for Busbridge.
- π Each phone has its own key. The pairing link works once, for 5 minutes. You see every paired phone on Settings, and Remove stops it at once.
- π§° A paired phone can control and read your devices, but not reprogram them. Busbridge passes on only what the app sends, in the form the app sends it, and refuses changing modules' addresses, names or logic. A phone can still set what the app can: heating setpoints, and a logic module's clock.
- ποΈ The database is readable only by the user Busbridge runs as, since it holds the keys. Where Busbridge can't make it so, as on Windows, the app's port stays closed.
What it doesn't protect:
- π The web pages have no login, and use plain HTTP. Anyone who can reach them, on your home network or your tailnet, can read the API key, change actions, and pair a phone of their own. Someone who can listen on your network can read the pages too.
- π The API over plain HTTP from the Internet: someone between IFTTT and you can read the key and send the request again. Use HTTPS, e.g. through a reverse proxy.
- π‘ A paired phone can switch everything the app can. Keep phones' screens locked, and remove a lost one on Settings. On the home network, the app talks to the bus directly, so Busbridge's limits only apply through the relay.
What you should do, most important first:
- π Treat the Wi-Fi password as the key to the house. Use WPA2 or WPA3 with a strong password, and put visitors and smart gadgets on a guest network that can't reach your home network.
- πͺ Open no ports to the Internet but the API, and only if IFTTT needs it. Use Tailscale for everything else, and turn off UPnP on the router.
- π₯ Keep your tailnet to your own devices. Tailscale's access rules can let only your phones reach the Pi's port 4568.
- β¬οΈ Keep Busbridge, Java and the Pi updated. Run
./update.sh, or turn on Check for updates in Settings. Running version 1.0.3 or earlier? Update: those versions have known security vulnerabilities.
For the careful: put HDL on its own network. With HDL's IP module on a network of its own, and Busbridge the only way in, gadgets and visitors on your home network can't reach the bus directly. The web pages have no login, though, so anyone who can open them can still run your actions and pair a phone.
Found a vulnerability? Please report it privately, as SECURITY.md describes.
β οΈ Use at your own risk. Busbridge is provided as is, without warranty of any kind (see the MIT license). You decide how it's reachable, and you're responsible for that. Like all software, it and the libraries it builds on will have weaknesses found over time, so keep it updated: run./update.sh, or turn on Check for updates in Settings to be told when there's a new version. And remember that anyone on your home network can control HDL Buspro directly, with or without Busbridge.
There could also be weaknesses in your router, IFTTT or Google Assistant. Read more about security and HDL Buspro in My dumb smart home.
You need Java 17 or later:
./gradlew buildThis runs the tests and builds the runnable JAR at build/libs/hdlserver-<version>.jar, which is what's released as
releases/hdlserver-LATEST.jar.
- π± My Android app for HDL Buspro
- βοΈ My dumb smart home, a blog post on HDL Buspro and security
- π¬ The demo on YouTube
- π’ HDL Automation
- π Changelog
Independent project, not affiliated with or endorsed by HDL Automation. HDL, Buspro and other trademarks belong to their owners.



