Saturday, April 24, 2021

Simple MQTT handler in bash

How to subscribe to an MQTT topic efficiently in bash.

This is hosted on a Raspberry Pi which controls a pool pump via a solid state relay on GPIO 4, and these topics :

  • /pool/pump/action 0  # Command, set externally (subscribed)
  • /pool/pump/state  1  # Feedback state (published)


''

#!/bin/bash -feu
set -o pipefail

mqtt()
{
action="$1"
shift
"mosquitto_${action}" -h 192.168.1.1 -p 8833 -t "$@"
}

handler()
{
new_state=$(echo "$1" | sed 's/[^01]//g')
[[ -z "$new_state" ]] && new_state="0"

if /usr/local/bin/gpio_out.sh 4 "$new_state"; then
echo "pump state change OK, now $new_state"
else
echo "ERROR: pump state change fail towards $new_state"
fi

# Ping back
real_state=$(cat /sys/class/gpio/gpio4/value)
mqtt pub '/pool/pump/state' -m "$real_state" -r
}

export -f mqtt
export -f handler


action={1-}
cfg=$(basename $0 .sh).service
if [[ $action = 'stop' ]]; then
systemctl stop $cfg

elif [[ $action = 'start' ]]; then
systemctl restart $cfg

elif [[ $action = 'install' ]]; then

cat > /etc/systemd/system/$cfg << EOF
[Install]
WantedBy=multi-user.target
[Service]
Restart=always
RestartSec=3
ExecStart=$(realpath $0)
#Type=simple
#Restart=on-failure
EOF

systemctl daemon-reload &&
systemctl restart $cfg &&
systemctl enable $cfg

else # here is the deal

# Run only once
LOCK_FILE="/var/lock/$(basename $0 .sh).lock"
exec 9> "$LOCK_FILE" && flock -n 9 || exit 5
trap "rm -f '$LOCK_FILE'; logger 'Stopped $0 watcher on $(date)'" EXIT
logger "Started $0 watcher on $(date)"

# Wait for orders
mqtt sub '/pool/pump/action' |\
xargs -n 1 -I {} /bin/bash -c 'handler "$@"' _ {}

fi

''

This small script embeds its own ''systemd'' config, just run it with ''install' as the sole parameter.