A Home Assistant custom integration that controls Schellenberg roller-shutter motors through a Schellenberg USB Funk-Stick. Exposes each paired motor as a standard HA cover entity with time-based position tracking, plus USB stick status sensors and an LED switch.
Based on the original work by Scott Downing (GimpArm), with calibration persistence introduced by ohlmannmichael-ai.
Warning: This integration is not affiliated with Schellenberg. The developers take no responsibility for anything that happens to your devices as a result of using this software.
- Controls Schellenberg roller-shutter motors over RF via the Schellenberg USB Funk-Stick
- Supports bidirectional motors (ROLLODRIVE PREMIUM and similar) that send movement events back to the stick — position tracking is event-driven
- Supports timed (non-bidirectional) motors that never confirm movement — position tracking is purely time-based using pre-measured travel times
- Time-based position tracking — open/close times measured via a built-in calibration flow; drive-to-percentage works on any calibrated motor
- Auto-pairing — put the stick into pairing mode from the HA UI; press the pairing button on the motor within 2 minutes
- Manual add — add motors already paired by other remotes, or non-bidirectional motors, by entering a two-character hex enumerator: a user-chosen id that the stick uses to address that motor
- Bind a physical remote — from a motor's Configure screen, bind a handheld remote by pressing its button twice; the remote then drives best-effort position tracking and a Home Assistant event entity for that motor. A multi-channel remote can bind each of its channels to a different motor independently — binding a second channel no longer collides with the first
- USB auto-discovery — HA detects the stick on plug-in (USB VID
16C0/ PID05E1, manufacturervan ooijen) and pre-fills the serial port - Stick status sensors — connection status, firmware version, and operating mode
- LED switch — toggle the USB stick LED on/off from HA
- Local control only — no cloud dependency (
iot_class: local_push) - Up to 240 device slots per stick (enumerators
0x10–0xFF; the integration allocates them automatically for auto-paired motors, starting at0x10)
- Home Assistant 2025.1.0 or later
- Schellenberg USB Funk-Stick (USB VID
16C0/ PID05E1, manufacturervan ooijen) connected to the HA host - Serial port access — the HA host user must be able to open the serial device (typically
/dev/ttyUSB0or/dev/ttyACM0on Linux) - HACS (for the recommended install path)
This integration is a custom HACS repository. Add it once, then install as usual:
- Open HACS in your Home Assistant sidebar.
- Click the three-dot menu (top right) and choose Custom repositories.
- Enter
hrabbach/ha-schellenberg-usb-plusas the repository and select Integration as the category. Click Add. - Search for Schellenberg USB+ in the HACS list, select it, and click Download.
- Restart Home Assistant when prompted.
Or use the one-click badge:
Copy the custom_components/schellenberg_usb/ folder from this repository into your HA config/custom_components/ directory, then restart Home Assistant.
- Go to Settings → Devices & Services → Add Integration and search for Schellenberg USB+.
- If the stick is already plugged in, HA may auto-discover it and pre-fill the serial port — confirm it. Otherwise, enter the port path (e.g.
/dev/ttyUSB0). - Go to the newly created Schellenberg USB+ hub and click + Add device.
- Choose Pair automatically or Add manually (for already-paired or timed motors).
- Calibrate the motor from its device page to enable position tracking.
Full step-by-step instructions, including calibration and troubleshooting, are in docs/GETTING-STARTED.md.
When you choose Pair automatically in the HA UI, the integration signals the USB stick to accept the next RF registration frame. You must also press the pairing button on the motor itself within 2 minutes.
The exact button combination varies by motor model:
Art.Nr.: 22567, 22576, 22578, 22726, 22727, 22728, 22767
- Press and hold the Sun (☀) button and the Up (▲) button simultaneously.
- Hold for 5 seconds until the LED flashes.
- The motor is now in pairing mode — it accepts the next registration frame from the stick.
Art.Nr.: 20106, 20110, 20406, 20410, 20610, 20615, 20620, 20640, 20710, 20720, 20740
Pairing is performed through the motor's connected Schellenberg remote or wall switch. Consult the remote's printed quick-start card for the pairing button sequence.
Art.Nr.: 21106, 21110, 21210, 21220, 21240
Pairing is driven by the connected control device (remote or timer switch). Refer to that device's manual for the exact sequence.
Motors in this category never send a pairing response — the in-app Pair automatically flow waits for the motor to transmit its device ID over RF, so a silent motor that was paired only to a physical remote will time out every time.
To add a silent motor you first perform wireless delegation pairing outside the integration (teaching the motor to accept commands from the stick), then register it in HA via Add manually.
When a timed motor has a bound physical remote registered in Home Assistant, the integration tracks position updates triggered by that remote. Because timed motors give no movement confirmation, this tracking is time-based and approximate — the position shown in HA reflects elapsed time since the button press, not a confirmed motor state. The position self-corrects the next time you open or close the motor through Home Assistant directly. If a remote press is missed (e.g. out of radio range), the position in HA will not update for that press.
Bind a remote from the motor's Configure screen (Bind a remote): press any button on the remote, then press the same button again to confirm. A remote is identified by its channel and its hardware id, not the id alone — so each channel of a multi-channel handheld remote can be bound to a different motor. If the same remote's other channel is already bound to a different motor, Home Assistant shows an Add channel confirmation instead of rejecting the bind; the other motor's existing binding is left untouched.
The two-character hex enumerator (e.g. 1A) is a user-chosen id that the stick assigns to a motor at pairing time. It is the stick's address for that motor — not an address the motor already has. When you add a motor via Pair automatically, the integration allocates the lowest free enumerator starting at 10 automatically. When you add a motor via Add manually (after delegation pairing below), you pick a unique value yourself and enter that same value in the HA form.
This procedure teaches a motor that is already paired to a physical Schellenberg 5-channel remote to also accept commands from the USB stick. Perform these steps before adding the motor in Home Assistant, with HA (and the integration) either stopped or not yet owning the serial port — the raw commands below are sent directly to the port.
Prerequisites: The HA host must have write access to the stick's serial device (e.g. /dev/ttyACM0 or /dev/ttyUSB0). Identify the correct path with ls /dev/tty* after plugging in the stick.
Step 1 — Pick a unique enumerator. Choose any two-character hex value in 02–FF that is not already used by another motor in HA. Avoid 00 and 01. Call it XX in the steps below (e.g. 1B).
Step 2 — Put the motor into programming mode using the 20016 remote.
- On the 20016 remote, select the channel that controls the target motor.
- Press the remote's P-button (programming button) — the remote LED blinks to confirm programming mode is active.
- Press Stop on the remote — the motor acknowledges with a brief jog or beep, indicating it is now in learn mode and ready to accept a new remote/stick ID.
Step 3 — Within ~10 seconds, send the two delegation frames from the HA host.
Replace XX with your chosen enumerator (e.g. 1B) and /dev/ttyACM0 with your actual port:
# Frame format: ss + {2-char enum} + 9 + {2-char command} + 0000
# CMD_PAIR = 60 (teach the motor to respond to enumerator XX)
echo 'ssXX9600000' > /dev/ttyACM0
# CMD_ALLOW_PAIRING = 40 (instruct the motor to accept the new pairing)
echo 'ssXX9400000' > /dev/ttyACM0Concrete example for enumerator 1B:
echo 'ss1B9600000' > /dev/ttyACM0
echo 'ss1B9400000' > /dev/ttyACM0Frame format reference (from
api.py):ss{enum:2}{repeat:1}{cmd:2}{pad:4}where repeat is always9and pad is always0000. Total: 11 characters. Example for enum1B:ss+1B+9+60+0000=ss1B9600000.
Step 4 — Confirm the motor learned the new id. The motor acknowledges again (jog/beep). Exit programming mode on the 20016 remote by pressing the P-button again.
Step 5 — Test with drive commands. Verify the motor responds to the stick before adding it in HA:
# CMD_UP = 01
echo 'ssXX9010000' > /dev/ttyACM0
# CMD_DOWN = 02
echo 'ssXX9020000' > /dev/ttyACM0
# CMD_STOP = 00
echo 'ssXX9000000' > /dev/ttyACM0Step 6 — Add the motor in Home Assistant. Go to Settings → Devices & Services → Schellenberg USB → Add device → Add manually (already paired). Enter XX (the same hex value used above) as the device enumerator, select timed (non-bidirectional) motor type, and complete the form. The integration will now send all commands to that enumerator.
- Keep the USB Funk-Stick within range (approximately 20 m indoors).
- Avoid metal obstructions between the stick and the motor.
- If pairing times out, move the stick closer and try again.
- If the model above does not match your motor, consult the printed instruction sheet included with the motor or the Schellenberg support site.
Note: The integration is model-agnostic — it only needs to receive the motor's RF registration frame. Any model that emits a compatible Schellenberg pairing frame will work.
| Type | How it works | Position tracking |
|---|---|---|
| Bidirectional | Motor reports started moving and stopped events back to the stick |
Event-driven — the integration starts and stops the timer automatically |
| Timed (non-bidirectional) | Motor gives no movement feedback | Time-based — the integration drives the motor and measures elapsed time between your button presses during calibration |
You select the motor type once when adding the device. Existing motors added before this distinction was introduced are treated as bidirectional.
For each USB stick (hub):
| Entity type | Name | Description |
|---|---|---|
| Sensor | Connection status | Whether the serial connection to the stick is active |
| Sensor | Firmware version | Stick firmware version string |
| Sensor | Operating mode | Current stick operating mode |
| Switch | LED | Toggles the USB stick LED on or off |
For each paired motor (subentry):
| Entity type | Description |
|---|---|
| Cover | Open / close / stop / set position (position tracking requires calibration) |
| Event | Only for timed (non-bidirectional) motors with a bound physical remote — fires on open, close, stop, and hold-up / hold-down remote button presses |
| Document | Contents |
|---|---|
| docs/GETTING-STARTED.md | Full install, pairing, calibration walkthrough, and troubleshooting |
| docs/CONFIGURATION.md | All configuration fields, calibration constants, protocol details |
| docs/ARCHITECTURE.md | System design, component map, data flow |
| docs/DEVELOPMENT.md | Local dev setup, build commands, code style |
| docs/TESTING.md | Running tests, coverage, CI integration |
| CONTRIBUTING.md | Quality gate commands and contribution guidelines |
See CONTRIBUTING.md for the development setup and the quality gate (tests, lint, type-check, and spell-check) every change must pass.
MIT — see LICENSE for details.
Issues and feature requests: github.com/hrabbach/ha-schellenberg-usb-plus/issues