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.
This small script embeds its own ''systemd'' config, just run it with ''install' as the sole parameter.