A balena user has found a clever way of intercepting the button press event in an app container that allows for any action to be triggered, while preventing the default system behaviour of powering the device down when the power button is pressed. It was tested on an Intel NUC, but the principle may apply to other platforms as well. They have kindly shared their solution and were happy for it to be posted here, so others can benefit too. Their use case was to trigger the WiFi Connect app when the power button was pressed, but anything else could be triggered instead.
The app container should have the ability to communicate with the host OS via DBUS, so it should either be privileged, or should have the “io.balena.features.dbus = true” label added to the docker-compose.yml file.
Then this shell script should be executed in the app container:
#!/usr/bin/env bash echo 'Starting App Container' # Set DBus address for Network Manager export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket # Prevent power button from shutting down CPU systemd-inhibit sleep infinity & # Start ACPI service to listen for power button presses service acpid start # Listen for ACPI events and run script if Power Button is pressed acpi_listen | while IFS= read -r line; do if [ "$line" = "button/power PBTN 00000080 00000000" ]; then echo 'Power button pressed, running script' # run your script here sleep 10 # Optional: reboot the system echo 'Rebooting System' dbus-send \ --system \ --print-reply \ --dest=org.freedesktop.systemd1 \ /org/freedesktop/systemd1 \ org.freedesktop.systemd1.Manager.Reboot break else echo "$line" fi done echo 'Restarting Container'
As coded it looks like the container is restarted on every ACPI event: perhaps it would be more efficient to have a loop in the script. But the key points are:
systemd-inhibit sleep infinity- disables the default power off system behaviour.
service acpid start- starts the acpi daemon in the app container
acpi_listen- listen for ACPI events like the power button press
- For the optional step of rebooting the system, what we usually recommend is using the balena supervisor API: POST /v1/reboot endpoint:
curl -X POST --header "Content-Type:application/json" \ "$BALENA_SUPERVISOR_ADDRESS/v1/reboot?apikey=$BALENA_SUPERVISOR_API_KEY"
BALENA_SUPERVISOR_API_KEY variables are predefined in balena containers. I think the advantage of the supervisor API is that the supervisor will first try to gracefully stop the app containers. The dbus method may be more aggressive – but I thought I would keep the dbus example too.