Bluetooth LE broadcast · no hub · no new hardware

A ceiling fan that
only listens.

The bedroom's Ohniyou ceiling fan came with an app — FanLampPro — and nothing else: no hub, no Wi-Fi, no cloud API. It's driven by Bluetooth LE broadcast-advertising: the app shouts commands into the air and the fan silently listens. It never advertises back, so a normal Bluetooth scan finds nothing to pair with. We taught Home Assistant to shout the same commands — from the Pi's own Bluetooth radio, sat in the same room — so the fan, its speed, its reverse and its dimmable downlight all landed in HA and Google, with no extra box on the wall.

Ohniyou 52" ha-ble-adv Pi Bluetooth no Broadlink

2entities (fan + light)
0new hardware
BLEbroadcast advert
1app press to learn
01 · The catch

A device with no handle to grab

Most smart kit here was freed by finding its LAN or radio protocol. The fan is stranger. It belongs to the same family as the house's reverse-engineered "FastCon" Bluetooth bulbs: control is one-way broadcast advertising. The FanLampPro app doesn't connect to the fan — it transmits encrypted BLE advertisement packets that any nearby fan in pairing range will act on. The fan is a pure receiver: it never advertises its presence, exposes no GATT services, and answers no scan. There is simply nothing there to discover and connect to the normal way.

Broadcast, not connect

Commands ride in the advertising channel itself, encrypted. There's no session, no bond — whoever transmits the right packet in range wins.

Invisible to a scan

Because the fan only ever listens, a Bluetooth scan turns up nothing. The usual "add device" flow has no candidate to offer.

No hub required

Early on this looked like an RF remote needing a Broadlink blaster. It isn't — the app path is pure BLE, so the fix needed no extra hardware at all.

02 · The integration

Teach Home Assistant to shout the same packets

The answer is NicoIIT's ha-ble-adv — a custom Home Assistant component that speaks exactly this broadcast-advertising family (FanLampPro, LampSmart Pro, Zhi Jia and friends). It transmits the commands straight from a Bluetooth radio HA already has: the Raspberry Pi's own adapter, which happens to sit in the same bedroom as the fan. No ESP proxy, no Broadlink, no bridge.

The component installs into /config/custom_components/ble_adv on the Pi; HA runs in a privileged, host-network container with the Pi's Bluetooth passed through, so it can open a raw HCI socket and transmit directly — no proxy in between.
How it was captured. There's nothing to scan for, so the pairing is done by imitation. HA's config-flow ("Duplicate paired phone-app config") listens on the Pi's Bluetooth while you press a button in FanLampPro standing next to the fan; it offers a couple of candidate code-sets, you pick the one that makes the fan's light blink, and that's the binding. One press, done — and because it's just a code-set, not a bonded connection, it's rock-solid afterwards.
03 · What landed

A real fan and a real light in HA

The binding produced two proper entities — a fan and a colour-temperature light — with the full feature set, not just on/off.

Fan
fan.fan_main_fan
On/off, speed %, breeze and sleep presets, and reversible direction (summer/winter) — the whole remote, as a Home Assistant fan.
Downlight
light.fan_main_light
The fan's built-in downlight as a dimmable colour-temperature light, warm through cool — a normal HA light entity.
The custom code

Where it plugs into the rest of the house

The component is third-party; the bits we wrote are how it slots into the panel and voice. The fan tile lives on the Lights tab, generated from a small Python model of the rooms — tap toggles it, hold opens the speed & direction control:

# _lights-view-gen.py — the "Kev's Bedroom" room card
("fan.fan_main_fan", "Ceiling fan", "mdi:ceiling-fan", "fan"),

# fans are deliberately kept out of the master "N of 21 lights on"
# count and the room "All off" — a fan isn't a light
ALL = [e for *_, ls in ROOMS for e,_,_,_ in ls if not e.startswith("fan.")]

Tap = on/off · hold = the fan's more-info card (speed slider + reverse) · excluded from the lights count & All-off.

And exposed to Google, so it answers to voice — the fan as a native Google fan (on/off and speed), the downlight as its own controllable light:

# configuration.yaml — google_assistant.entity_config
fan.fan_main_fan:
  name: Bedroom Fan
  aliases:
    - Ceiling Fan
    - Bedroom Ceiling Fan
  expose: true
light.fan_main_light:
  name: Bedroom Fan Light

"Turn on the Bedroom Fan" · "set the Bedroom Fan to 50%" · "turn off the Bedroom Fan Light". Presets and reverse stay on the panel's control card.

One thing to watch. The Pi's single Bluetooth radio is shared with the reverse-engineered bulb mesh, so the fan and those bulbs take turns on the same adapter — fine in practice, but the reason a spare ESP32 sits on the bench as a fallback broadcast proxy if contention ever bites.

App-free air

The fan joined the house without buying a thing.

A device with no hub, no cloud and no connectable radio — pulled into Home Assistant by having the Pi it already runs on transmit the same Bluetooth adverts the app does. Speed, presets, reverse and a dimmable downlight, on the wall panel and in Google, and the FanLampPro app never has to be opened again.

ha-ble-adv · Pi Bluetooth·0 new hardware·see it on the Wall panel