Control Brel (Dooya DDxxxx) motorized blinds locally from a Raspberry Pi using an RFXtrx433XL USB stick — no cloud, no hub required.
The Brel DD-7002B hub is cloud-only and has no local API (especially after recent firmware updates). This solution bypasses the hub entirely by using an RFXtrx433XL USB stick to communicate directly with the blind motors over 433MHz FSK.
Note: Standard 433MHz devices (RFLink, cheap GPIO transceivers) will NOT work. Brel/Dooya motors use FSK modulation which requires the RFXtrx433XL specifically.
- Raspberry Pi (any model)
- RFXtrx433XL USB stick (with firmware version 2025 or higher)
- Available at rfxcom.com (~€110)
- Also sold as RFXusb-RFX433 (newer model)
- Brel motorized blinds with a DDxxxx-compatible remote (e.g. DD2700, DD2702)
- The original Brel remote (needed once for pairing)
The RFXtrx433XL may ship without firmware. Flash it once using RFXmngr on Windows:
- Download RFXmngr from rfxcom.com/downloads
- Download the latest RFXtrx433XL_ProXL1 firmware from the same page
- Plug the RFXtrx433XL into a Windows PC
- Open RFXmngr → Firmware tab → select the
.hexfile → click Upload - After flashing, plug into your Raspberry Pi
Linux alternative: You can flash using Mono with
sudo mono RFXflash.exe /dev/ttyUSB0 RFXtrx433XL_ProXL1_XXXX.hex— however this may fail on newer Debian/Raspberry Pi OS due to missing Visual Basic assemblies in Mono 6.8.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtrequirements.txt currently includes:
flask(API service)pyserial(RFXtrx433XL serial communication)
ls /dev/ttyUSB0If not present, check dmesg | tail -20 after plugging in the USB stick.
Use the unified pairing script:
# up/down channel only
python3 pair.py rolluik1 updown
# percentage channel only
python3 pair.py rolluik1 position
# both channels
python3 pair.py rolluik1 bothValid devices: screen, rolluik1, rolluik2.
Before pairing:
- Stop the API/service so
/dev/ttyUSB0is free. - Put the target motor in pairing mode (P2 workflow on remote).
Legacy scripts are kept for troubleshooting and renamed with a legacy- prefix:
legacy-pair-rolluik1.pylegacy-pair-rolluik2.pylegacy-pair-screen.pylegacy-pair-rolluik1-updown.pylegacy-pair-rolluik1-position.pylegacy-pair-rolluik2-updown.pylegacy-pair-rolluik2-position.py
Use pair.py as the default path for pairing.
You need to pair the RFXtrx433XL with each blind motor once. This uses the motor's built-in "learn extra remote" function — no physical access to the motor is needed, only the original remote.
You choose the ID yourself — any arbitrary ID works. The motor learns and remembers it. You do not need MAC addresses or any IDs from the Brel app.
- Select the channel for the blind you want to pair on your original remote
- Press the P2 button twice on the back of the remote
- The blind motor will jog (move slightly) to confirm it entered pairing mode
- Within 3 seconds, run the relevant
python3 pair.py <device> <mode>command - The motor will jog again to confirm successful pairing
import serial
# Choose any ID you like — use the same ID for all blinds,
# differentiate them by unit code (1, 2, 3 etc.)
ID = [0x10, 0x20, 0x30, 0x40]
UNIT_CODE = 0x01 # change to 0x02, 0x03 etc. for each blind
with serial.Serial('/dev/ttyUSB0', 38400, timeout=2) as ser:
ser.flushInput()
# Step 1: Pair for up/down/stop (BlindsT21)
packet = bytes([
0x09, 0x19, 0x15, 0x00,
ID[0], ID[1], ID[2],
(ID[3] & 0xF0) | (UNIT_CODE & 0x0F),
0x03, # P2 pair command
0x00,
])
ser.write(packet)
print(f"BlindsT21 pair sent: {' '.join(f'{b:02X}' for b in packet)}")
import time
time.sleep(1)
with serial.Serial('/dev/ttyUSB0', 38400, timeout=2) as ser:
ser.flushInput()
# Step 2: Pair for position/percent (DDxxxx 0x31)
packet = bytes([
0x0C, 0x31, 0x00, 0x00,
ID[0], ID[1], ID[2], ID[3],
UNIT_CODE,
0x03, # P2 pair command
0x00, 0x00, 0x00,
])
ser.write(packet)
print(f"DDxxxx pair sent: {' '.join(f'{b:02X}' for b in packet)}")Run the pairing script immediately after the motor jogs (within ~3 seconds). The motor will jog again to confirm pairing.
Tip: Run the pairing script in advance and have it ready to execute. The timing window is short.
Edit the DEVICES dictionary at the top of brel.py to match your setup:
DEVICES = {
'screen': (0x41, 0x01), # (BlindsT21 unit byte, DDxxxx unit code)
'rolluik1': (0x42, 0x02),
'rolluik2': (0x43, 0x03),
}The unit byte format for BlindsT21 is (ID[3] & 0xF0) | unit_code. With ID 10 20 30 40 and unit codes 1/2/3 this gives 0x41, 0x42, 0x43.
# Up / Down / Stop
python3 brel.py screen up
python3 brel.py rolluik1 down
python3 brel.py rolluik2 stop
python3 brel.py all up
# Set position (0 = fully open, 100 = fully closed)
python3 brel.py screen position 50
python3 brel.py rolluik1 position 0
python3 brel.py all position 7509 19 15 00 [ID1] [ID2] [ID3] [ID4|unit] [cmd] 00
Commands:
0x00 = Up
0x01 = Down
0x02 = Stop
0x03 = P2 pair
0C 31 00 00 [ID1] [ID2] [ID3] [ID4] [unit] [cmd] [percent] 00 00
Commands:
0x03 = P2 pair
0x04 = Set percent (0–100)
| Problem | Likely cause | Fix |
|---|---|---|
/dev/ttyUSB0 not found |
Driver not loaded | Check dmesg after plugging in |
| ACK received but blind doesn't move | Not paired with this ID | Re-run pairing procedure |
| Motor doesn't enter pairing mode | P2 not pressed correctly | Press P2 twice, wait for jog each time |
| Position command ignored | Only paired for BlindsT21 | Also pair using the DDxxxx (0x31) packet |
| Works for one blind, not others | Wrong channel selected on remote | Switch channel on remote before pairing |
- Raspberry Pi 4 / Pi 5
- RFXtrx433XL with firmware 2026
- Brel motorized roller blinds and sun screens with DD2700 remote
- Raspberry Pi OS (Debian 12 Bookworm)
Packet format documented by the RFXCOM community at rfxcom.com and the Home Assistant community.