Control the MoonHalo backlight of a BenQ RD280UG monitor from Hubitat and Google Home.
The MoonHalo is the LED backlight on the rear of the BenQ RD280UG monitor. BenQ provides two ways to control it: the Function Bar on the monitor and the Display Pilot 2 desktop application. BenQ documents no smart-home integration for it, so it cannot be added to Google Home as a light or driven by home automation rules.
BenQ MoonHalo Bridge exposes the MoonHalo to a Hubitat Elevation hub as a dimmable, color-temperature light. It has two parts:
- Driver: one Groovy file, installed on the Hub.
- Bridge: a Python service, installed on the Windows PC the monitor is connected to.
The Hub sends HTTP requests to the Bridge over the LAN. The Bridge converts each request into a DDC/CI write to the monitor.
| Component | Requirement |
|---|---|
| Hub | Hubitat Elevation. Tested on a C-7, platform 2.4.3.177. |
| Monitor | BenQ RD280UG, connected to the PC by a DisplayPort or HDMI cable that carries DDC/CI. No other monitor is tested. |
| PC | Windows with Python 3.12 or later and the py launcher. Tested on Windows 11. |
| Network | Hub and PC on the same LAN. The Hub must reach TCP port 5000 on the PC. |
| Google Home (optional) | The Google Home Community app. |
Install the Bridge, then the Driver. Each step is marked PC or Hub. For details of any PC step, see the Bridge README.
-
PC - Clone the repository, or download and unpack the ZIP, and open the Bridge folder.
git clone https://github.com/RBILLC/Hubitat.git cd Hubitat\Bridges\BenQ_MoonHalo -
PC - Install the dependency (Flask).
py -m pip install -r requirements.txt -
PC - Create the configuration file.
copy config.example.json config.jsonIn
config.json, setallowed_ips,allowed_macsandhub_ipto the Hub's IP and MAC address. The IP is on the Hub's Settings > Hub Details page.arp -aon the PC lists the MAC next to that IP. -
PC - Confirm the Bridge detects the monitor.
py -m moonhalo_bridge monitorsExpected last line:
selected: BenQ RD280UG on \\.\DISPLAYn (by edid). -
PC - Register and start the logon task. The script requests administrator approval, creates the scheduled task
MoonHaloBridgeand checks that the Bridge answers..\install_task.ps1
-
PC - Allow the Hub through Windows Firewall. Replace
192.168.1.10with the Hub's IP. See Windows Firewall rule.netsh advfirewall firewall add rule name="MoonHalo Bridge (Hub)" dir=in action=allow protocol=TCP localport=5000 remoteip=192.168.1.10 profile=any -
Hub - Import the Driver. Open Drivers Code, click New Driver, click Import, paste the URL, import, and click Save.
https://raw.githubusercontent.com/RBILLC/Hubitat/main/Drivers/BenQ_MoonHalo_Bridge_Driver.groovy -
Hub - Create the device. Open Devices, click Add Device > Virtual, enter a name, and select the type BenQ MoonHalo Bridge.
-
Hub - On the Preferences tab, enter the PC's IP in Bridge IP address. Change Bridge port only if the Bridge's
portis not 5000. Click Save Preferences. -
Hub - Verify on the Commands tab:
- On lights the halo.
- Set Level changes its brightness.
- Under Current States,
bridgeLinkisonlineandmonitorLinkisok.
| Command | Behavior |
|---|---|
on |
Turns the halo on at the last level. The Bridge stores the level; the Hub does not send one. |
off |
Dims the halo to its lowest step, then turns it off. Level and color temperature are retained. |
setLevel(level, rate) |
Level 0 to 100. The halo moves to the nearest of its ten brightness levels; level reports the value sent. Level 0 is off. A level sent while the halo is off turns it on. |
setColorTemperature(kelvin, level, rate) |
The Bridge maps the Kelvin value to one of seven color steps between 2700 K and 6500 K. colorTemperature reports the Kelvin of that step: 2700 reports 2971. Turns the halo on if it is off, unless Enable color pre-staging is set. |
setColorTempStep(step) |
Sets the color step directly, 1 (warm) to 7 (cool). |
refresh |
Reads the Bridge's status. |
setBridgeAddress(ip, port) |
Called by the Bridge to announce its address. Can be run manually. |
Transitions. Every change is written as a sequence of hardware steps.
- With a rate: the rate is the total time of that change, in seconds.
- Without a rate: the Default transition (ms) preference applies. It is the time of a full brightness sweep, so a shorter change takes proportionally less time.
- A value of 0 applies the change at once.
Use the community Google Home Community app. It provides on/off, brightness, voice control and the white-temperature slider. Hubitat's built-in Google Home app provides on/off and brightness only; it shows no color-temperature control for color-temperature bulbs.
Setup procedure: Google Home setup guide.
Device type settings in the community app:
| Setting | Value |
|---|---|
| Device type name | Bulb (any name) |
| Google Home device type | Light |
| Traits | On/Off, Brightness, Color Setting |
| Color Setting | Color Temperature Control only |
| Minimum / maximum color temperature | 2700 / 6500 |
| Color temperature attribute | colorTemperature |
| Set color temperature command | setColorTemperature |
Notes:
- After each move, the slider settles on the nearest of the seven color steps.
- Do not share the device through both apps.
| Preference | Default | Description |
|---|---|---|
| Bridge IP address | none | IPv4 address of the PC running the Bridge. Used until the Bridge announces its address. |
| Bridge port | 5000 | Port the Bridge listens on (port in config.json). |
| Announce timeout (seconds) | 200 | After the first announcement, the time without an announcement or a reply before the Bridge is marked offline. 0 disables the check. Set it above the Bridge's announce_seconds. |
| Request timeout (seconds) | 5 | Time to wait for the Bridge to answer a command before marking it offline. |
| Poll interval | 5 minutes | Status poll interval: disabled, or 1, 5, 10, 15 or 30 minutes. |
| Enable color pre-staging | off | Accept a color temperature while the halo is off without turning it on. |
| Default transition (ms) | 300 | Time of a full brightness sweep when a command carries no rate, 0 to 60000. 0 applies changes at once. Blank uses the Bridge's transition_seconds. |
| Enable debug logging | on | Writes debug lines to the hub log. Turns off after 30 minutes. |
| Enable descriptionText logging | on | Writes one info line to the hub log per change. |
The color temperature range is set on the Bridge: kelvin_min and kelvin_max in config.json,
default 2700 and 6500.
Address announcement (optional): the Bridge can report its current address to the Hub through the Maker API, so the PC needs no DHCP reservation. See Letting the Hub find the Bridge.
| Attribute | Values | Description |
|---|---|---|
switch |
on, off |
State of the halo. |
level |
0 to 100 | Level last commanded. 0 while the halo is off. |
colorTemperature |
Kelvin | Kelvin of the current color step. |
colorName |
text | Hubitat's name for that Kelvin, for example Soft White. |
bridgeLink |
unknown, online, offline |
Whether the Hub reached the Bridge on its last attempt. While offline, switch and level keep their last values. |
monitorLink |
unknown, ok, failed, unreachable |
Whether the Bridge reached the monitor on its last attempt. failed logs one warning; commands are still sent. unreachable is set while bridgeLink is offline. |
bridgeAddress |
ip:port |
Address the Bridge last announced. |
| Variable | Description |
|---|---|
bridgeVersion |
Version the Bridge reports with every reply. |
lastMonitorError, lastMonitorErrorAt |
Last DDC/CI error and its time. Retained after recovery. |
lastSeen, lastAnnounce |
Time of the last reply and of the last announcement. |
announcedIp, announcedPort, typedAddress |
The announced address and the address typed in the preferences. |
The Bridge is Python source in
Bridges/BenQ_MoonHalo/moonhalo_bridge. Its only
dependency is Flask. It includes no compiled or third-party executable.
| Aspect | Behavior |
|---|---|
| Listening port | TCP 5000 by default. HTTP GET only. |
| Access control | Allowlist of the Hub's IP address and MAC address (resolved through the PC's ARP table), set in config.json. Other callers receive 403. Exceptions: requests from the PC itself (allow_loopback, default on) and /health, which is read-only. |
| Monitor writes | Two VCP registers on the RD280UG, identified by its EDID: one for power, one for brightness and color temperature. No other monitor and no other setting is written. |
| Network traffic | LAN only. No internet connection and no telemetry. |
| Process | A scheduled task in the logged-on user's session, without administrator rights. It starts at logon and stops at logoff. A Windows service is not used: services run in session 0, where the display functions are not documented to work. |
| Address announcement | Optional. With a Maker API token configured, the Bridge calls one command on one Hub device. The token is stored in config.json and is not logged. Without a token, the Bridge makes no outgoing calls. |
| Files written | state.json (last level and color step) and bridge.log (requests and monitor writes), in the Bridge folder. |
- Resolution: ten brightness levels and seven color steps. This is a hardware limit; the steps are visible in a long transition.
- Platform: Windows only.
- PC off or logged out:
bridgeLinkisoffline. - Monitor asleep: commands are refused,
monitorLinkisfailed, andswitchis unchanged. Commands work again when the monitor wakes. No restart is needed. - Other monitors: only the RD280UG is tested. For another MoonHalo monitor, set
monitor_productinconfig.jsonto its EDID product code, whichpy -m moonhalo_bridge monitorsprints.
For symptoms and fixes, see the troubleshooting table in the Bridge README.
| Part | Current version |
|---|---|
| Driver | 0.0.17 |
| Bridge | 0.0.12 |
- The Driver and the Bridge are versioned separately.
- The Driver requires Bridge 0.0.9 or later. An older Bridge is flagged in
bridgeVersionand with one warning in the hub log. - A Bridge newer than the Driver is compatible.
- Release notes: GitHub Releases.
To update:
-
Hub: import the Driver again from the same URL and save.
-
PC: pull the new code, then restart the task.
schtasks /end /tn MoonHaloBridge schtasks /run /tn MoonHaloBridge
- Questions and discussion: Hubitat community forum thread, PLACEHOLDER-FORUM-THREAD-URL
- Bugs: GitHub Issues
- Website: PLACEHOLDER-WEBSITE-URL
This is a community driver. It is not supported by Hubitat or BenQ.
MIT. See LICENSE.txt.
Also in this repository: a Tuya TS0601 soil moisture sensor driver,
Drivers/Tuya_TS0601_Soil_Sensor_Driver.groovy.
See its forum thread.



