I have a Broan/NuTone ChromaComfort/Sensonic bathroom fan with the integrated Bluetooth speaker, white light, and RGB light.
I’ve wanted to get it properly integrated into Home Assistant for a while, but there’s an annoying limitation: the fan expects a Bluetooth connection for control, and tying up that connection with a controller can interfere with actually using the Bluetooth speaker.
So I went down the rabbit hole of figuring out the protocol and eventually built a Linux bridge that does both.
The end result is a small Linux box that maintains the Bluetooth connection to the ChromaComfort and exposes:
- Fan on/off in Home Assistant
- White light on/off + brightness
- RGB light on/off + color + brightness
- Wall RGB mode
- Real-time-ish device status
- MQTT/Home Assistant Discovery
- Connection/packet/ACK diagnostics in HA
- The built-in speaker as an AirPlay destination
- Spotify/iPhone volume control through AirPlay
The interesting part is that the Linux host maintains both Bluetooth Classic RFCOMM/SPP for controlling the fan and A2DP for audio at the same time.
The architecture ended up looking like this:
ChromaComfort-Sensonic
/ \
RFCOMM / SPP A2DP
/ \
chromacomfort_bridge.py BlueZ
| |
MQTT PipeWire/WirePlumber
| |
Home Assistant Shairport Sync
|
AirPlay
|
Spotify/iPhone
There were a few fun quirks along the way.
The control protocol requires retransmitting commands until the fan acknowledges them. RGB colors use the fan’s “favorite color” functionality. The periodic status packets expose brightness but don’t appear to expose the current RGB values, so the bridge remembers successfully commanded colors.
Linux Bluetooth was probably the most entertaining part. After reboot, the device could report Connected: yes because the RFCOMM connection was established while the A2DP audio sink didn’t actually exist. BlueZ would also occasionally come up in a state where bluetoothctl just hung.
So the project now includes boot-time health checks that verify the things we actually care about instead of trusting Bluetooth’s connected flag. If BlueZ is wedged it gets recovered, and if the PipeWire A2DP sink doesn’t appear, the audio connection is retried until it does.
It’s now survived reboot testing with both HA control and AirPlay audio coming back automatically.
I packaged everything up here:
For Debian/Ubuntu I’ve also put together an installer:
git clone https://github.com/Relkci/ChromaComfort-Python-Control.git
cd ChromaComfort-Python-Control
sudo bash scripts/install-linux.sh
It prompts for the ChromaComfort Bluetooth MAC, MQTT information, Home Assistant naming, and AirPlay speaker name.
Huge credit to Taylor Finnell’s previous ChromaComfort reverse-engineering work, which gave me the foundation for understanding the Bluetooth serial protocol.
I expanded on that while figuring out the status packets, HA/MQTT bridge, Linux RFCOMM behavior, brightness/color control, and simultaneous A2DP audio.
If anyone else has one of these fans, I’d be particularly interested in hearing whether different hardware revisions expose the same RFCOMM channel/protocol and whether the installer works cleanly on other Linux hardware.


