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.
