spd-say in bash script does not work when called from crontab but works from terminal
https://askubuntu.com/questions/1562735/spd-say-in-bash-script-does-not-work-when-called-from-crontab-but-works-from-ter
My system is Lubuntu 22.04.5 LTS on a laptop.
Because I would like to keep my battery as healthy as possible, I want to make sure that the charging level stays between certain boundaries, but the hardware in my laptop does not support battery charge limits, so I turned to make use of a bash script (which I found at Github and modified it to my needs) which is called via crontab every 5 minutes. It then gives me a notification which tells me the battery charge in %, with a message warning me when it becomes to low (when the charger is not connected) or when it is too high (when the charger is plugged in), the third possibility in the notification text is that it just tells me the charge when it is between the given boundaries in the script. This works okay, but the trouble started when I added a spoken message to the script by using a line in the script that is meant to do just that.
The spoken message uses spd-say and the bash script works fine with spd-say giving the audio only when I call the script from terminal. But when the script is called from crontab it also makes the notification text appear alright, but then there is no sound at all to be heard.
I also tried aplay, which plays soundfiles and this gives a similar result, when the script is called from terminal it works, but not when called from crontab.
Here is the script, it is located at /usr/local/bin and it is called battmon.sh:
# ------------------------------------------------------------------
# Script Name: battmon.sh
# Description: A Simple Bash Script for Battery Level Charge
# Notifications
# Website: https://gist.github.com/ostechnix
# ------------------------------------------------------------------
# Define thresholds
HIGH_THRESHOLD=80
LOW_THRESHOLD=40
LOGFILE="/tmp/battmon.log"
# Get the battery level
LEVEL=$(acpi -b | awk -F', ' '{print $2}' | tr -d '%,')
# Ensure LEVEL is a valid number
if [[ "$LEVEL" =~ ^[0-9]+$ ]]; then
# Check for high battery level
if [ "$LEVEL" -ge "$HIGH_THRESHOLD" ]; then
echo "$(date) - Battery at $LEVEL%. Sending high battery notification..." >> "$LOGFILE"
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 notify-send -t 0 "Accu is vol" "De accu is nu opgeladen tot boven het ingestelde maximale niveau van $HIGH_THRESHOLD % en is nu $LEVEL % , ontkoppel de lader !" >> "$LOGFILE" 2>&1
spd-say -t female1 -w "stop direct met opladen ! de accu is vol, stop meteen met opladen, want de accu is meer dan $LEVEL% procent vol!"
fi
# Check for low battery level
if [ "$LEVEL" -le "$LOW_THRESHOLD" ]; then
echo "$(date) - Battery at $LEVEL%. Sending low battery warning..." >> "$LOGFILE"
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 notify-send -t 0 "Accu is laag" "De accu is nu ontladen tot onder het ingestelde minimale niveau van $LOW_THRESHOLD % en is $LEVEL %. Sluit nu de lader aan!" >> "$LOGFILE" 2>&1
spd-say -t female1 -w "begin nu direct met opladen ! de accu is leeg, begin meteen met opladen, want de accu heeft nog maar $LEVEL% procent lading !"
fi
# Check for good battery level
if [ "$LEVEL" -ge "$LOW_THRESHOLD" ] && [ "$LEVEL" -le "$HIGH_THRESHOLD" ] ; then
echo "$(date) - Battery at $LEVEL%. Sending normal battery level message..." >> "$LOGFILE"
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 notify-send -t 10000 "De accu is geladen op een veilig niveau en is nu $LEVEL% procent" >> "$LOGFILE" 2>&1
aplay /home/paul/Muziek/spraak/ttsmaker-file-2026-1-10-21-23-47-accuniveau-test-2.wav
spd-say -t female1 -w "de accu heeft nog steeds een goede lading van op dit moment $LEVEL% procent"
fi
fi
I used
crontab -e
and entered this line:
*/5 * * * * /usr/local/bin/battmon.sh >> /tmp/battmon.log 2>&1
I also tried to use the cron in another way, as I understood that there is the difference in cron for the user and the system, so to try it in the system way and I did:
sudo nano /etc/crontab
and added the same line there:
*/5 * * * * /usr/local/bin/battmon.sh >> /tmp/battmon.log 2>&1
But to no avail...
At this moment I have no clue how to fix this, maybe it is not possible at all to call these sound outputting programs from crontab, but maybe someone does know how to do it right. In either case I am very curious about the why and how.
EDIT-1:
The answer knu gave seems to do a great deal of the trick, i.e. when I followed his answer and called the script from terminal everything worked, notification and sounds (I must add that strangely enough it was the notification that worked yesterday and not the sounds, but this morning it was the other way around, which I could and still can not get my head around, but after adding the line:
export XAUTHORITY=/run/user/1000/gdm/Xauthority
that was fixed (called from terminal).
But in the log there was an error message "Cannot autolaunch D-Bus without X11 $DISPLAY" which was only resolved after adding the line:
export DISPLAY=:0.0
but I also saw another error in the log, note that this error only occurs when the script is called from cron, not from terminal:
"
GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.Notifications was not provided by any .service files
"
for which I found a solution here
But still things are not going as wished:
- although everything works when called from terminal, when called from cron the script does not work! (my fault: I accepted the answer as the solution, but this is still not the case...) No notification, no sound.
- the log now shows another error (again only when the script is called from cron):
"Error calling StartServiceByName for org.freedesktop.Notifications: Failed to execute program org.freedesktop.Notifications: No such file or directory"
- After searching for a solution for the error mentioned under 2 above, I found the exact same suggestion as a solution that resolved the previous error, but of course it is clear that this does not work for this second error, because after following the solution for the first error this second one still exists !
So I was too fast in thinking this question has been resolved. I still am looking for a solution...
EDIT-2:
The missing notification has been solved by installing notification-daemon:
sudo apt-get install notification-daemon
Altough the sound output is still missing, the log now does not show any error messages after the installing of the daemon.
EDIT-3:
I did not mention this before, but I do now: I did removed the:
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000
that stood in front of the notification lines like in:
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 notify-send -t 0 "Accu is vol" "De accu is nu opgeladen tot boven het ingestelde maximale niveau van $HIGH_THRESHOLD % en is nu $LEVEL % , ontkoppel de lader !" >> "$LOGFILE" 2>&1
because at the time I noticed that after adding
export XAUTHORITY=/run/paul/1000/gdm/Xauthority
export DISPLAY=:0.0
this was not necessary anymore, but I just found out that the suggestion that steeldriver gave in the comments was right, it was still needed before the statements that gives the speech output.
So now it reads for instance:
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 spd-say -t female1 -w "de accu heeft nog steeds een goede lading van op dit moment $LEVEL% procent"
Concluding:
The script that is functioning alright now on my system looks this way:
#!/usr/bin/env bash
# ------------------------------------------------------------------
# Script Name: battmon.sh
# Description: A Simple Bash Script for Battery Level Charge
# Notifications, modified from the website below.
# Website: https://gist.github.com/ostechnix
# ------------------------------------------------------------------
# Define thresholds
HIGH_THRESHOLD=80
LOW_THRESHOLD=40
LOGFILE="/tmp/battmon.log"
# Get the battery level
LEVEL=$(acpi -b | awk -F', ' '{print $2}' | tr -d '%,')
export XAUTHORITY=/run/user/1000/gdm/Xauthority
export DISPLAY=:0.0
# Ensure LEVEL is a valid number
if [[ "$LEVEL" =~ ^[0-9]+$ ]]; then
# Check for high battery level
if [ "$LEVEL" -ge "$HIGH_THRESHOLD" ]; then
echo "$(date) - Battery at $LEVEL%. Sending high battery notification..." >> "$LOGFILE"
notify-send -t 0 "Accu is vol" "De accu is nu opgeladen tot boven het ingestelde maximale niveau van $HIGH_THRESHOLD % en is nu $LEVEL % , ontkoppel de lader !" >> "$LOGFILE" 2>&1
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 spd-say -t female1 -w "stop direct met opladen ! de accu is vol, stop meteen met opladen, want de accu is meer dan $LEVEL% procent vol!"
fi
# Check for low battery level
if [ "$LEVEL" -le "$LOW_THRESHOLD" ]; then
echo "$(date) - Battery at $LEVEL%. Sending low battery warning..." >> "$LOGFILE"
notify-send -t 0 "Accu is laag" "De accu is nu ontladen tot onder het ingestelde minimale niveau van $LOW_THRESHOLD % en is $LEVEL %. Sluit nu de lader aan!" >> "$LOGFILE" 2>&1
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 spd-say -t female1 -w "begin nu direct met opladen ! de accu is leeg, begin meteen met opladen, want de accu heeft nog maar $LEVEL% procent lading !"
fi
# Check for good battery level
if [ "$LEVEL" -ge "$LOW_THRESHOLD" ] && [ "$LEVEL" -le "$HIGH_THRESHOLD" ]; then
echo "$(date) - Battery at $LEVEL%. Sending normal battery level message..." >> "$LOGFILE"
notify-send -t 10000 "De accu is geladen op een veilig niveau en is nu $LEVEL% procent" >> "$LOGFILE" 2>&1
DISPLAY=:0 XDG_RUNTIME_DIR=/run/user/1000 spd-say -t female1 -w "de accu heeft nog steeds een goede lading van op dit moment $LEVEL% procent"
fi
fi