Hello everybody.
Recently I’ve started getting acquainted topic of home automation and, after deciding to not go with any proprietary solutions for obvious reasons, went with the Raspberry Pi 4 as the base computer and Zigbee XBee WRL-15128 S2C module, connected to the serial port GPIO pins. My first try was OpenHAB and I didn’t really have good experience with it - adding the devices was hit or miss and since I’ve bought bunch of Aqara devices without realizing they aren’t really following the ZCL specification, it was a miracle if I managed to merely turn on the wall plug from the web GUI. I’ve stumbled upon Home Assistant, decided to give it a try and in less than one hour I had all my devices connected into one network (thank you ZHA device handlers!), with Xbee module fully configured and automations created. After all the days I spent configuring OpenHAB, that really felt like a miracle. What’s more, the HassOS feels much more appropriate for embedded solution, being a lightweight Buildroot-built distro instead of Raspbian fork the openHABian is. And on top of that, instead of the whole stack of Java solutions like OSGi and Apache Karaf it uses my beloved Docker (I work as a DevOps and yes, I’m a Docker fanboy )!
I’ve decided to go further with my DIY project of creating universal home automation gateway and the next step was creating a power button. Raspberry Pi devices have a system of device tree overlays which allows one to modify various aspect of the hardware and the ways it integrates with the operating system. There is a gpio-shutdown one and according to its documentation:
Name: gpio-shutdown
Info: Initiates a shutdown when GPIO pin changes. The given GPIO pin
is configured as an input key that generates KEY_POWER events.
This event is handled by systemd-logind by initiating a
shutdown. Systemd versions older than 225 need an udev rule
enable listening to the input device:
ACTION!="REMOVE", SUBSYSTEM=="input", KERNEL=="event*", \
SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", \
ATTRS{keys}=="116", TAG+="power-switch"
This overlay only handles shutdown. After shutdown, the system
can be powered up again by driving GPIO3 low. The default
configuration uses GPIO3 with a pullup, so if you connect a
button between GPIO3 and GND (pin 5 and 6 on the 40-pin header),
you get a shutdown and power-up button.
Load: dtoverlay=gpio-shutdown,<param>=<val>
Params: gpio_pin GPIO pin to trigger on (default 3)
active_low When this is 1 (active low), a falling
edge generates a key down event and a
rising edge generates a key up event.
When this is 0 (active high), this is
reversed. The default is 1 (active low).
gpio_pull Desired pull-up/down state (off, down, up)
Default is "up".
Note that the default pin (GPIO3) has an
external pullup.
debounce Specify the debounce interval in milliseconds
(default 100)
I know HassOS uses systemd, but from what I checked, there is no sign of systemd-logind there. I’m not trying nor asking to add it - systemd is heavy enough for the embedded solutions and adding one of its components solely for supporting power button is a bit too much. So, I’ve checked the systemd-logind udev rule which is normally responsible for handling the KEY_POWER events - it can be found on the Raspbian:
ACTION=="remove", GOTO="power_switch_end"
SUBSYSTEM=="input", KERNEL=="event*", ENV{ID_INPUT_SWITCH}=="1", TAG+="power-switch"
SUBSYSTEM=="input", KERNEL=="event*", ENV{ID_INPUT_KEY}=="1", TAG+="power-switch"
LABEL="power_switch_end"
I’ve enabled gpio-shutdown overlay by adding dtoverlay=gpio-shutdown
line to the config.txt file on the boot partition, created the following udev rule and placed it in /etc/udev/rules.d/99-gpio-power-switch.rules
file in the HassOS system (the root system, not one of the Docker containers):
ACTION!="remove", SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="116", RUN+="/bin/sh -c 'date >> /mnt/data/udev.test'"
As you can see, instead of calling /usr/sbin/shutdown
I’m just writing the execution time to the text file. However, nothing is happening when I press the button - no event is logged when I’m running udevadm monitor
and no new row is added to the text file. What’s more, the new row is always added on the system startup and after calling udevadm trigger
- which means that if I call /usr/sbin/shutdown
there, the system will be powered off immediately after startup. The button itself is working - since it connects the GPIO3 and GND pins, it’s able to wake up the RPI from the halt state, so I’m sure it’s connected correctly.
I didn’t try to wire up the shutdown button to Home Assistant and to be honest, I don’t want to - I’m aiming for the power button to be independent from the system software and be handled by the system itself, so that it works even when I stop the Home Assistant and/or the Docker daemon. The button seems to be visible to the system after adding the gpio-shutdown overlay (here is the result of executing udevadm info /dev/input/event0
):
P: /devices/platform/soc/soc:shutdown_button/input/input0/event0
N: input/event0
L: 0
S: input/by-path/platform-soc:shutdown_button-event
E: DEVPATH=/devices/platform/soc/soc:shutdown_button/input/input0/event0
E: DEVNAME=/dev/input/event0
E: MAJOR=13
E: MINOR=64
E: SUBSYSTEM=input
E: USEC_INITIALIZED=1751688
E: ID_INPUT=1
E: ID_INPUT_KEY=1
E: ID_PATH=platform-soc:shutdown_button
E: ID_PATH_TAG=platform-soc_shutdown_button
E: DEVLINKS=/dev/input/by-path/platform-soc:shutdown_button-event
Home Assistant also seems to discover the button:
Did anybody try to achieve the same thing as I do with the gpio-shutdown overlay? Maybe you have any idea what I’m doing wrong here? This is my first attempt on playing with the udev rules and kernel events, so my attempts are based on more-or-less conscious attempts of glueing together stuff from the documentation, manpages and whatever I find on the internet.