Feed the Birds

MQTT Action Check

I've been noticing a few of my Zigbee switches being a bit weird recently, but only the ones on Zigbee2MQTT. Just to be sure the messages are getting sent over the network properly I threw together this quick script which runs constantly on a Raspberry Pi.

It just listens for messages from my main switch and button devices, then reads the action value reported by Zigbee2MQTT using jq and throws it out to me using the wonderful Pushover service.

#!/usr/bin/env bash

## action_check.sh v0.01 (6th October 2025)
##  Sends the action of a given button press received over MQTT to Pushover

device_models=('E1766' 'E1812' 'WXKG06LM' 'WXKG07LM' 'WXKG11LM' 'WXKG12LM')

while true
do
    mosquitto_sub -h "192.168.1.1" -t "zigbee2mqtt/#" -R | while read -r payload
    do

        if [[ "${payload:0:1}" == "{" ]]; then

            this_model="$(echo "${payload}" | jq -r '.device.model')"

            if [[ " ${device_models[@]} " =~ " ${this_model} " ]]; then

                action_value="$(echo "${payload}" | jq -r '.action')"

                if [[ -n "$action_value" ]] && [[ "$action_value" != "null" ]]; then

                    friendlyName="$(echo "${payload}" | jq -r '.device.friendlyName')"
                    #echo "$friendlyName $action_value"

                    curl -s -o /dev/null \
                        --form-string "token=your_app_token" \
                        --form-string "user=your_user_token" \
                        --form-string "title=$friendlyName" \
                        --form-string "message=$action_value" \
                        --form-string "priority=-2" \
                        https://api.pushover.net/1/messages.json   

                fi

            fi

        fi

    done
    sleep 2
done

The lowest priority -2 setting means that the messages don't generate any notifications, but they are added to the Pushover app's "channel" and can be viewed with a pull-to-refresh. This way I don't get spammed whenever someone presses a light switch. But if you'd prefer that, just nudge the value up.

Of course, systemd takes care of ensuring it's running with a user service. This lives as ~/.config/systemd/user/action-check.service:

[Unit]
Description=MQTT Action Check
After=network-online.target

[Service]
ExecStart=/path/to/action_check.sh
Restart=always
RestartSec=5s
Type=simple
RemainAfterExit=yes
WorkingDirectory=/tmp
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=default.target

This Raspberry Pi was a pretty fresh install, so I needed to enable lingering to ensure it started without any login:

loginctl enable-linger $USER

Then all the usual systemd setup stuff:

systemctl --user daemon-reload
systemctl --user enable action-check.service
systemctl --user start action-check.service
systemctl --user status action-check.service

Naturally, once I had this up and running the problem resolved itself by magic.

Side Note, Not Magic

As a conclusion to this tale, it turned out that the inconsistent problem was due to my implementation of a debounce function in my Hubitat drivers. The debounce worked fine, but once enough time had elapsed the check would overflow the long variable. When the resulting negative value was compared to the debounce window it was, of course, always less than that duration. So any action during that period of time would be discarded.

Once the value rolled back around to positive the switches would work perfectly once again. But discovering this silly mistake took a little time given the number of steps, and potential points of failure, between the press of a wireless button and the programmed action.

Yes, I am aware of how daft this all is. But it's still cool.

← Recent Articles
October 06, 2025