Building a NAS control panel for macOS with BitBar

published on in category macOS , Tags: macos nas bitbar

After finishing with the hardware and software parts of my new NAS, I decided to append another little project which is aimed to provide a simplified control panel for macOS in the menu bar on the upper right of the screen.

Objective

What I wanted to achieve is a possibility to mount my various shares with one click as well as having controls for power on/off and SSH. Additionally the control should indicate whether the NAS is currently powered on or not.

BitBar

Realizing something like this in Objective-C or Swift would not be complicated but I decided to give BitBar a try. BitBar is basically a tool that loads all executable files at a given location, assumes that they provide a specific format on stdout and renders that as a menu bar app. This means that you can use almost any language including BaSH, Python, PHP, Perl, Ruby and much more to implement a menu bar app.

Installation

When using Homebrew, BitBar can be installed like this:

brew cask install bitbar

Otherwise just download it from https://getbitbar.com/.

Expected format

BitBar expects a executable printing something like the following structure to stdout:

{Text visible in the menu bar itself which can be clicked to open the menu}
---
{Text only}
{Function}|bash="uname -a" terminal=true
{Styled text}|color=red

This file should be located in the plugin folder that can be configured in the BitBar application itself. The filename also has to follow a specific convention:

<filename>.<refresh-time>.<ext>

So for my case this would be:

nas.2min.sh

2min means that the script reloads itself every two minutes (which in my case is for checking if the NAS is still online).

Writing the script

The functiality should be pretty easy to implement so used a simple shell script. All I use in this script is ping, ssh, open, osascript and wakeonlan.

Detect online status

This is usual unix stuff, so all I do is sending a single ICMP package using ping to check if the system responds, throwing away all the output and saving the exit code in $RES.

#!/bin/bash
ping -c 1 -t 1 {ip} > /dev/null 2>&1
RES=$?

Then I can just check if the exit status was 0 and paint the menu bar item either green or red according to the state.

if [ $RES -ne 0 ]; then
	echo "NAS|color=red"
else
	echo "NAS|color=green"
fi

When saving this and reloading BitBar (opening a BitBar menu item and press CMD+R for example), you should already see your menu bar app :-). Remember to chmod +x the script.

Putting it all together

The following image shows how I implemented the formatted output and how it reflects on the UI:

NAS control BitBar plugin

And this is the whole script including all the functionality:

#!/bin/bash
#
# <bitbar.title>nas</bitbar.title>
# <bitbar.version>1.0</bitbar.version>
# <bitbar.author>David Prandzioch</bitbar.author>
# <bitbar.author.github></bitbar.author.github>
# <bitbar.desc></bitbar.desc>
# <bitbar.image></bitbar.image>
# <bitbar.abouturl></bitbar.abouturl>
#

if [[ "$1" = "mountstuff" ]]; then
    open afp://192.168.0.21/Stuff
    exit
fi

if [[ "$1" = "mountbackups" ]]; then
    open afp://192.168.0.21/Backups 
    exit
fi

if [[ "$1" = "wake" ]]; then
    /usr/local/bin/wakeonlan -i 192.168.0.255 xx:xx:xx:x:xx:xx
    osascript -e 'display notification "NAS startup requested" with title "NAS"'

    ssh nas "uname -a"
    while test $? -gt 0
    do
      sleep 5
      ssh nas "uname -a"
    done

    osascript -e 'display notification "NAS is online" with title "NAS"'
    exit
fi

if [[ "$1" = "shutdown" ]]; then
    ssh nas "shutdown -p now"
    exit
fi

ping -c 1 -t 1 192.168.0.21 >/dev/null 2>&1
RES=$?

if [ $RES -ne 0 ]; then
    echo "NAS|color=red"
else
    echo "NAS|color=green"
fi
echo ---
if [ $RES -eq 0 ]; then
    echo ---
    echo "Shut down|bash=$0 param1=shutdown terminal=false refresh=true"
    echo ---
    echo "SSH|bash=\"ssh nas\" terminal=true"
    echo ---
    echo "Mount Stuff|bash=$0 param1=mountstuff terminal=false"
    echo "Mount Backups|bash=$0 param1=mountbackups terminal=false"
else
    echo "Wake|bash=$0 param1=wake terminal=false refresh=true"
fi