I’m using BalenaOS and building a docker container with some python that senses when a button is pressed and then is supposed to send this command:
dbus-send --system
--print-reply=literal
--dest=org.freedesktop.login1
/org/freedesktop/login1
"org.freedesktop.login1.Manager.PowerOff" boolean:true
I can open a terminal inside this container, run this command, and it DOES work exactly as intended. However, pressing the button only results in this in error messages (shown at bottom of post)
Here is my setup:
dockerfile
FROM balenalib/%%BALENA_MACHINE_NAME%%-debian-python:3.7.4
# Enable systemd init system
ENV INITSYSTEM off
# Set the working directory
WORKDIR /usr/src/app
RUN install_packages git dbus gnome-common
RUN apt-get update
RUN apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0
# Upgrade pip
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --user -r requirements.txt --no-cache-dir --disable-pip-version-check
--index-url https://www.piwheels.org/simple
# Copy everything into the container
COPY . ./
#Make sure scripts in .local are usable:
ENV PATH=/root/.local/bin:$PATH
ENV DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket
# Start application
CMD ["bash", "start.sh"]
requirements.txt
RPi.Gpio
dbus-python
start.sh
#!/usr/bin/env bash
## connect to the host's system bus from the application container
export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket
EDIT
I abandoned dbus-python
for pydbus
It seems to yield a better result and seems to simplify the problem but still spits an error. More info on the difference between the two can be found here https://wiki.python.org/moin/DbusExamples
Here is the edited python
button.py
import RPi.GPIO as GPIO
import time
import pydbus
import gi
# Set GPIO mode: GPIO.BCM or GPIO.BOARD
GPIO.setmode(GPIO.BOARD)
# Set pin 5 an an input, and enable the internal pull-up resistor
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP)
oldButtonState1 = True
while True:
buttonState1 = GPIO.input(5)
if buttonState1 != oldButtonState1 and buttonState1 == False :
bus = pydbus.SystemBus()
logind = bus.get('.login1')['.Manager']
logind.PowerOff()
oldButtonState1 = buttonState1
time.sleep(1)
The output is now:
21.12.19 11:33:53 (-0800) button logind.PowerOff()
21.12.19 11:33:53 (-0800) button File "/root/.local/lib/python3.7/site-packages/pydbus/proxy_method.py", line 62, in __call__
21.12.19 11:33:53 (-0800) button raise TypeError(self.__qualname__ + " missing {} required positional argument(s)".format(-argdiff))
21.12.19 11:33:53 (-0800) button TypeError: org.freedesktop.login1.Manager.PowerOff missing 1 required positional argument(s)
21.12.19 11:33:59 (-0800) button button.py:12: RuntimeWarning: A physical pull up resistor is fitted on this channel!
21.12.19 11:33:59 (-0800) button GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP)
If I had to guess I’d say I’m doing something wrong in the python script. Not utilizing dbus correctly.
2
Answers
Thanks to stovfl
The issue was:
interface.PowerOff()
was missing True. It should beinterface.PowerOff(True)
The correct dockerfile is:
My requirements.txt
The correct python script is
Links that helped me find the answers:
https://wiki.python.org/moin/DbusExamples
https://fhackts.wordpress.com/2019/08/08/shutting-down-or-rebooting-over-dbus-programmatically-from-a-non-root-user/
https://pygobject.readthedocs.io/en/latest/getting_started.html
Raspbian bullseye solution:
Sudo Edit
/usr/share/polkit-1/actions/org.freedesktop.login1.policy
to change the required security settings in https://www.freedesktop.org/software/systemd/man/org.freedesktop.login1.html#Security e.g. to allow "poweroff"Python:
It’s not ideal for a multi-user system to allow unchallenged access to poweroff but works fine for my purpose of shutting down a robot with a remote control key