1-Line of Bash to add Apps to the System Tray

Putting your commonly used apps and scripts into the Linux system tray could be quite useful.

In this blog I’ll look at two approaches:

  • alltray – a command line utility to dock any program into the system tray
  • yad – (Yet Another Dialog) tool is a Bash GUI builder that also supports trays notifications.

The alltray approach is dead simple and it works for any linux application or script. The yad utility offers a little more functionality by adding the ability to create command line dialogs and it can dynamically change the tray icon, actions, menus and tool tip.

Alltray

To install alltray in Debian/Raspian/Ubuntu:

sudo apt install alltray

An simple alltray example that calls an xterm window with the top utility (to show top running processes):

# Use alltray to put a terminal window app in the the tray
#  usage: alltray [options] ["] <program_name> [parameters] ["]
#
alltray  "xterm -hold -T 'Top Processes' -e 'top'"

As a default alltray will use the icon of the command that is being called.

For this example I used xterm to open a new terminal with the options of: -hold (keep teminal open) , -t ( add title) and -e (execute a command).

If you want to change the font name and size, use the -fa and -fs options, for example:

xterm -hold -fa Monospace -fs 14  -T "Top Processes" -e  "top"

Alltray also supports a custom icon and right-click menus, an example of this would be:

# Show a tray item with a custom icon and right-click menu options
#   syntax for adding menus is: --menu "menu-label: command"
#
alltray  "xterm -hold -T 'Top Processes' -e  'top'" \
  -i /usr/share/icons/Adwaita/256x256/legacy/face-glasses.png \
  --menu "Disk Usage:xterm -hold -T 'df' -e 'df'" \
  --menu "Sensors:xterm -hold -T 'Sensors' -e 'sensors' " 

For this example a custom icon (face-glasses.png) is added along with 2 right-click menu options.

YAD – Yet Another Dialog

The yad utility is a command line dialog tool that supports a good selection of different dialog types, also yad can be configured for system tray applications without needing alltray.

For Debian/Raspian and Ubuntu systems yad can be installed by:

sudo apt install yad

Yad has a lot of options, (see the man pages or help for more details). To create a simple two button dialog:

# Show a simple YAD dialog
yad  --text="SOME TEXT"  --title="My Dialog"

To put this simple yad dialog on the system tray with a custom icon:

# Create a YAD system tray item to call a YALL dialog
#
yad --notification --image="gtk-execute" \ 
  --command="yad --text='SOME TEXT' --title='My Dialog' " \
  --text="My Tooltip"

Icons are fairly easy to manage using the yad tool: yad-icon-browser .

Like alltray, yad supports menus, below is a menu example:

# Create a YAD system tray item with a right-click menu
#
yad --notification --image="gtk-execute" \
 --command="yad --text='SOME TEXT' --title='My Dialog'" \
 --menu="Memory! yad --text='$(vmstat)' --title=VMSTAT \
        | Sensors! yad --text='$(sensors)' --title=Sensors \
        | USB ! yad --text='$(lsusb)' --title=USB \
        | Quit ! killall yad" \
 --text="My Tooltip"

The syntax for menus is:

menu=STRING
              STRING must be in the form:
              menu_label1[! action1[! icon1]]|label2[! action2[! icon2]]....   
              Menus are separated with `|' or --separator  argument.
              Menu items are separated with `!' or --item-separator argument.

For this menuing example I passed the output from command line tools like vmstat, sensors and lsusb to the yad –text parameter.

Unlike alltray, yad doesn’t have a built in quit menu option, but this functionality can be added with:

Quit ! killall yad

Remotely Change a YAD Tray Item

The yad notification option has a –listen parameter that allows commands to be sent from stdin (standard input) to yad in the form command:args. Possible commands are icon, tooltip, visible, action, menu and quit.

The yad stdio can be redirected a named pipe and this will enable other bash scripts to be able to send it commands. Below is a basic Bash script that creates a named pipe variable (mytraypipe=”/tmp/tray1.pipe”) and then it creates the named pipe if it doesn’t exist.

The Bash command: exec 1<> $mytraypipe , redirects stdio (file 1) to the named pipe. The final step is to call the yad with <&1 , to redirect the stdio into the command.

#!/bin/bash
#
# dyn_tray.sh - create a system tray item that can be modified
#             - write changes to the named pipe: $mytraypipe 
#
mytraypipe="/tmp/tray1.pipe"

# Make the pipe if required
if ! test -e "$mytraypipe"; then
  mkfifo $mytraypipe
fi

# redirect the stdio (file 1) to the named pipe
exec 1<> $mytraypipe

# create the notification icon
yad --notification                  \
    --listen                        \
    --image="emblem-colors-grey"              \
    --text="Dummy tooltip"   \
    --command="yad --text='Test Tray App' " <&1

Below is an example that changes the icon and tool tip for the earlier Bash script. This first step is to define a variable with the correct named pipe, after this commands can be send with an echo statement to the named pipe.

Summary

If you are looking for a quick way to pull together some commonly used apps and scripts both alltray and yad offer a simple 1 line of Bash solution.

If you want to dynamically change the tray item then yad is the tool for you.

Bash with MQTT

I’m working on re-purposing an old router. One of my goals is to bring sensor and performance data from the router to a Home Assistant node. The router doesn’t not have a lot of space so I’d prefer to use Bash rather than Python for MQTT communications.

While I was working on the project I wanted to use some simple tools to view the data, unfortunately I wasn’t able to find any information on how to make a good MQTT client in Bash.

This blog documents how I used Bash to show bar charts of MQTT data.

Setup

The first step is to install the Mosquitto command line tools on the OpenWrt router:

# Update the package manager list
opkg update
# Install the MQTT client utiliy
opkg install mosquitto-client-nossl

When I have some time I’d like to look at making some more MQTT Bash scripts that could be used with file input.

The next step is to install the Mosquitto client on my Linux PC and Raspberry Pi:

sudo apt-get install mosquitto-clients

There are a number of MQTT brokers that can be used, both Home Assistant and Node Red have reliable brokers. The Mosquitto broker can also be loaded on a Linux, MacOS and Windows node.

Publish MQTT Data in Bash

The Mosquitto client can be used to both publish and subscribe to MQTT data.

For my router project, I wanted the: temperature (from a USB thermometer), idle time, % used space and available space. Below is the script that passed the data to the mosquitto_pub (publishing) tool:

#!/usr/ash
#
# mqtt_data.sh - send data to MQTT broker
#
# Get Data values
temp=$(/usbstick/temper.sh)
idle=$(vmstat | awk '{ if (NR==3) print $15}')
used=$(df | awk '{if (NR==4) printf "%.f\n", $5 }')
space=$(df | awk '{if (NR==4) printf "%.1f\n", $4/1000 }')

echo "$temp $idle $used $space"

# Publish Data
server="192.168.0.111"
pause=2

mosquitto_pub -h $server -t rtr_temp -m $temp
sleep $pause
mosquitto_pub -h $server -t rtr_idle -m $idle
sleep $pause
mosquitto_pub -h $server -t rtr_used -m $used
sleep $pause
mosquitto_pub -h $server -t rtr_space -m $space

I added a pause between each publish to let me watch the actions.

The cron utility can be used to schedule the running of the script. Once a minute is the fastest time available with cron, so if faster times are needed the script could cycle with a while loop.

Read/Subscribe to MQTT Data

The mosquitto_sub client tool can be used to read or subscribe to the data.

To look at the router points:

$ service="192.168.0.111"
$ mosquitto_sub -h $server -v -t rtr_idle -t rtr_temp -t rtr_used -t rtr_space

rtr_temp 26.25
rtr_idle 100
rtr_used 66
rtr_space 1.1

The -v (–verbose) option show output with the topic names and the values. Topics, -t option, can be put on the command line or passing in as a file.

For single point monitoring the Zenity utility can be used. This utility is typically preloaded on most Linux and Rasp Pi systems. A script to create a progress bar dialog with MQTT data is:

# Send MQTT values to a progress bar
( 
  while :; do
  msg=$(mosquitto_sub -h 192.168.0.111 -C 1 -t rtr_temp) 
  echo $msg
  echo "#$msg  Deg C"
  done
  ) | zenity --progress  --title="Router External Temperature"

Multipoint Progress Dialogs

The Zenity utility can only manage single point progress bars. For multiple point progress bars the YAD (Yet Another Dialog) package can be used.

To install YAD on Raspberry Pi’s and Ubuntu: sudo apt-get install yad

Below is some script that will show multiple points in a YAD dialog:

#!/usr/bash
#
# mqtt_bars.sh - Show multiple MQTT Topics on a Dialog
#
server="192.168.0.111"
topics=("rtr_idle" "rtr_temp" "rtr_used" )
scale=(100 40 100 )
units=( '%' 'degC' '%'  )
title="Router MQTT Points"

#Build topic and yad strings
yadstr=" "
for i in ${topics[@]}; do
  topstr="$topstr -t $i"
  yadstr="$yadstr --bar=$i"
done

echo "Press [CTRL+C] to stop..."
# Cycle thru 1 message at a time to YAD 
(
while : 
do 
  msg=$(mosquitto_sub -h $server -v -C 1 $topstr) 
  IFS=' ' read -a data <<< "$msg" 
  # match returned msg to order of bars, write value/label
  for i in "${!topics[@]} "
  do 
    if [ "${data[0]}" = "${topics[i]}" ]
    then 
      let j=i+1 ; # YAD indices start at 1
      # Rescale bar to defined scale
      barsize=$(bc <<< "${data[1]}*100/${scale[i]}")
      #barsize=100
      echo "$j:$barsize"
      echo  "$j:#${data[1]} ${units[i]}"
  fi
  done
done 
)  | yad --multi-progress $yadstr --title $title

Final Comments

There are probably lots of good 3rd party tools like Gnuplot that could be connected to mosquitto_sub to show real charts.