0

I was making a battery notifier script for my raspberry pi3. The script is executing when I do

/usr/bin/python /home/pi/Documents/shutdown.py

and showing the popup notifications.  However the service is not executing it or not showing the notification.  I can see the python process if I do sudo systemctl status battery-notifier.service.

battery-notifer.service

[Unit]
Description=Battery Notifier

[Service]
Type=simple
WorkingDirectory=/home/pi/Documents
ExecStart=/usr/bin/python /home/pi/Documents/shutdown.py

[Install]
WantedBy=multi-user.target

shutdown.py

import raspiupshat
import statistics
import subprocess
from time import sleep
raspiupshat.init()
while(True):
    voltagesList = []
    sleep(0.5)
    currentVoltage = raspiupshat.getv()
    voltagesList.append(currentVoltage)
    medianVoltage = statistics.median(voltagesList)
    if(medianVoltage>4):
        subprocess.Popen(["notify-send","Battery Full!"])

This is the status of the service when I do sudo systemctl status battery-notifier.service:

● battery-notifier.service - Battery Notifier
   Loaded: loaded (/lib/systemd/system/battery-notifier.service; enabled)
   Active: active (running) since Sat 2017-07-15 04:05:18 UTC; 48min ago
 Main PID: 28384 (python)
   CGroup: /system.slice/battery-notifier.service
           └─28384 /usr/bin/python /home/pi/Documents/shutdown.py

Jul 15 04:05:18 raspberrypi systemd[1]: Started Battery Notifier.
  • 2
    Possible duplicate of Using notify-send with cron – jasonwryan Jul 15 '17 at 5:38
  • how do I set the dbus session env variable in python script? Tried subprocess.call("export DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-goOEk5dZcK,guid=9c1f14175e6be0992b16e5155969b46c",shell=True) ,but it didn't work. – avistein Jul 15 '17 at 6:38
  • Maybe, this points to a design issue. A command which is executed by a system process (by systemd) is supposed to directly manipulate the session of a user. What if there are multiple users logged in? Maybe it's more appropriate if you either implement the whole battery checker to be started when the user logs in, or you make the systemd task just write a message to the system-wide dbus (via dbus-send) and each logged in user must then launch a program (for example in Python via using the dbus package) which listens to that message to display something on the screen of their desktop? – akraf Jul 15 '17 at 12:44
  • Finally it worked! Yes you are right @akraf .Mentioning the User=pi in the service worked as a charm! Please edit your original answer with this so that it can help others too. – avistein Jul 15 '17 at 21:32
  • Glad that it worked! However, that was not my intention, I did not even know a User= systemd parameter existed :-). So the credits would be undeserved. My idea was to develop two separate programs, the first at system level which deals with checking the battery status and the second started at user login which is notified by the first and displays the message. – akraf Jul 18 '17 at 21:57
2

Edited to add: Your program tries to access the graphical desktop of a user (pi), however, it is itself executed by systemd. That is, it will run as root per default and not as the user pi. Moreover, there might not even be a graphical desktop yet at the time this is started.

Therefore, you could go several paths:

  • Start the program not via systemd, but when the logged in user starts the graphical desktop. Therefore, you need to put the program start into the file ~/.xinitrc or ~/.xprofile. See the Arch Wiki on Autostart
  • Add a line User=pi to your systemd service. This is the solution you mentioned in the comment to your original post
  • systemd can be configure to be executed in a user-mode (as opposed to system mode) when the user logs in. You could let your service be started by that way. See the Arch Wiki on systemd/User

Original answer:

See this SO question. There is an env= parameter in the subprocess.Popen(.) function which can be used.

Alternatively, you could use the feature of sh that if you assign a variable immediately before a call, that variable is set (only) for that call:

myvar=world
# cannot use echo $myvar, because $myvar would be substituted before echo is launched
myvar=hello declare -x myvar 
echo $myvar

gives

declare -x myvar="hello"
world

Link to corresponding chapter in POSIX spec

So for you:

subprocess.call("DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-goOEk5dZcK,‌​guid=9c1f14175e6be09‌​92b16e5155969b46c notify-send 'Battery full!'",s‌​hell=True)

The call you mentioned in the comment to your question does not work because it leads to the following process

  • subprocess.Popen('export DBUS_SESSION_BUS_ADRESS=..., shell = True)

    • Launch a shell
    • Set the environment variable DBUS_SESSION_BUS_ADRESS=... for this shell and all sub-shells (that's what export does).

      What it does NOT: Set the variable for all shells there are on the machine!

    • Close the shell. Now the value of DBUS_SESSION_BUS_ADRESS is lost again.
  • subprocess.Popen(['notify-send',...], shell=True)
    • Launch a shell
    • The export call of the previous shell has no power here because these two shells are completely unrelated
    • Launch notify-send as in your original question

So the setting of the environment variable has no effect here.

  • Unfortunately it still doesn't work.I used subprocess.Popen(['notify-send', 'Battery Full!'],env={'DBUS_SESSION_BUS_ADDRESS':'unix:abstract=/tmp/dbus-RTLFkePIFz,guid=c576b8a74fdd67955e5abbb05969f3cd'}) However running the python script alone is working and was working before DBUS_SESSION_BUS_ADDRESS added too! – avistein Jul 15 '17 at 11:15
  • Hm... you could try the other variables which were mentioned in @jasonwryan's comment, for example DISPLAY or XAUTHORITY... it would be awesome if you could leave a short notice if any of those works... – akraf Jul 15 '17 at 12:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.