MQTT shutter bridge for home assistant with Velux KLF 150 support
npm install @uncaught/gpio-shutter-bridgeThis is an implementation of a bridge between shutter controls and home assistant.
In particular I have connected one Velux KLF 150 gateway and one Schellenberg shutter remote to my Raspberry Pi using the GPIO pins.
But you can use this for Velux only, of course. You will need 3 GPIO pins for each Velux shutter and you can use up to 5 shutters with the KLF 150 in this setup.
No soldering required if you stick with Velux only - everything is just screws.
- Supports Velux KLF 150 or any 3-button remote control
- MQTT interface with auto-discovery for home assistant
- Positioning of the Velux shutters (time-based)
``mermaid
flowchart LR
HA[Home Assistant/MQTT] <--> Bridge[THIS LIBRARY]
Bridge <--> GPIO[Raspberry Pi GPIOs]
GPIO --> Relays[Relay Modules]
Relays --> KLF[Velux KLF 150]
Relays --> Remote[Schellenberg Remote]
KLF <--> Velux[Velux Shutters]
KLF --> GPIO
Remote --> Balcony[Balcony Shutter]
style Bridge fill:#ff9900,stroke:#333,stroke-width:2px,color:#fff
`
This is how everything looks before "unboxing" it into a more permanent shape:
Just to give you an insight, you can pobably use this project with any other hardware. It could make sense to buy a complete ESP32 GPIO board instead of using a Raspberry Pi.
- 1 Schellenberg shutter motor in my balcony door
- 1 Schellenberg remote control
- Which I opened up and soldered contacts to all three buttons (up/stop/down) as well as the power supply.
- This way I can supply power directly via the 3V output pin of the raspberry pi without needing batteries.
- I'm trusting you with this definitely professional piece of art.
- 3 Velux covers/shutters on my roof windows
- Velux KLF 150 gateway
- See https://www.velux.com/klf150
- Before buying this, I also tried soldering contacts to the Velux remotes (model 3UR B01 WW), but I broke the first...
- 9 relay modules
- If you use a Raspberry PI, you need relays that work with 3V and it helps if they already have a pull-up resistor because the GPIO pins can have a floating voltage when the PI boots and they are not yet assigned as outputs, triggering the relay when it shouldn't.
- I bought this 10 pack on amazon
- You will need 2 relays per Velux shutter, so for my three windows I needed 6 relays.
- Additionally I needed 3 relays for my Schellenberg remote control.
- If you find a board with multiple 3V relays on it, go ahead. I just screwed mine together on a wooden board.
- Raspberry Pi Model B
- Yes, the first version of the Raspberry Pi with the single armv6 CPU!
- Still had this lying around, so why waste it.
- I got node 21 running on it for this project.
- One USB power supply for the Raspberry Pi
- I used one of my many 2A supplies, not the 1.4A one that comes with the Velux KLF 150.
- The Pi then powers the relays, the KLF 150 and the Schellenberg remote.
- Connect all VCC pins of the relays to a 3V pin of the Raspberry Pi.
- I have daisy chained them together. Only the most left relay is connected to the Pi.
- Connect all GND pins of the relays to a GND pin of the Raspberry Pi.
- Again daisy chained.
- Connect each IN pin of the relays to one GPIO pin of the Raspberry Pi.
- If you use all 10 relays, for all 5 shutters, I'm suggesting pins 2, 3, 4, 17, 27, 22, 10, 9, 11, 8.
This is for the default configuration as described in the Velux KLF 150 manual. Meaning: You have 2 output pins and 4 input pins per shutter (A, B, C, D, E). For each shutter, the first input pin makes the shutter open, the second input pin makes it close, and both together make the shutter stop. The output pins for each shutter should close on success (also the default behavior).
- Connect all 5 bottom pins of the output (A-E) together and to GND.
- Connect all 5 top pins of the output (A-E) to any GPIO pin.
- I'm suggesting pins 14, 15, 18, 23, 24.
- The bottom line of all input pins are for GND. However, since the cabling comes in pairs anyway and you need to connect them each to one of the relays, I just used all the existing cables.
- So connect all 10 inputs to the 10 relays, the top one to NO (normally open) and the bottom one to COM (common).
- You will need to have node installed.raspi-gpio
- I have to use node 21, but it should work with newer versions as well.
- Make sure you have either or pinctrl (newer) available.raspi-gpio get
- Check with or pinctrl get.onoff
- Either one is used to set the GPIO input pins to the "pull up" mode. This is not handled in the -library I'm using.pinctrl
- is newer, but requires permissions./dev/gpio*
- So you either need to be root, or make sure you have access to all devices.gpio
- E.g. use the group:ll /dev/gpio*
- Check and see that every device is owned by the gpio group and has g+rw permissions.sudo chgrp gpio /dev/gpio
- If not, use and/or sudo chmod g+rw /dev/gpio.gpio
- You can add yourself to the group with sudo usermod -a -G gpio $USER.pinctrl get
- Verify with that you can use the tool without further permissions.~/shutter
- Go into a folder where you wish to install this project (e.g. ).npm install @uncaught/gpio-shutter-bridge
- Install the library with run.mjs
- Create a javascript file (e.g. ), require my library and call it with your shutter-pin-layout:
`js
import {createVeluxShutters, initRuntime, initMqtt} from '@uncaught/gpio-shutter-bridge';
const {onDispose} = initRuntime();
initMqtt(
createVeluxShutters(
[
{ident: 'Velux_A', up: 2, down: 3, input: 14},
{ident: 'Velux_B', up: 4, down: 17, input: 15},
{ident: 'Velux_C', up: 27, down: 22, input: 18},
{ident: 'Velux_D', up: 10, down: 9, input: 23},
{ident: 'Velux_E', up: 11, down: 8, input: 24}, //same row!
],
onDispose,
),
onDispose,
{url: 'mqtt://your-mqtt-or-home-assistant'},
);
`
- The ident should match /[a-zA-Z][a-zA-Z0-9_-]*/.
- See my personal example for a few more details.
Auto start with screen and crontab
I'm using screen to keep the process running and to check in on its output.
I created an additional start.sh file:
`bash`
#!/usr/bin/env bash
set -e
selfDir=$(dirname $(readlink -f $0))
cd $selfDir
screen -dmS shutter node run.mjs
Then added @reboot /home/nc/shutter/start.sh to my crontab -e.
If you are unfamiliar with screen, you can detach from a session with ctrl+a+d and reattach with screen -r shutter.
I tried to dockerize this project, but I was not able to get it to work with only specific mapped devices. The pinctrl kept saying "No GPIO chips found". I've only managed to get it working with the --privileged flag, which for me, kind of defeats the purpose of running this inside docker.
If you find a way to get it working, please let me know, I'll add it as an example.
My attempts
I tried based off of this:
`Dockerfile
FROM node:25.0.0-trixie-slim
RUN apt update && apt install -y git build-essential cmake \
&& git clone https://github.com/raspberrypi/utils.git \
&& cd utils/pinctrl \
&& cmake . \
&& make \
&& make install
ENTRYPOINT ["bash"]
`
Building with docker build -t gpio . and running with docker run --rm -it --device /dev/gpiochip0 --device /dev/gpiochip1 --device /dev/gpiochip2 --device /dev/gpiomem --cap-add SYS_RAWIO gpio`