60ac42e711
zigbee-monitor.yaml: - Single click (action=single) toggles Squiggle, Sideboard Lamp, Bamboo Lights - Press-hold-release (action=release after hold) toggles DJ Booth only - Two filter functions: "Single click" and "Hold release", button-in fan-out wires to both - DJ Booth added to zigbee-state-in and ui-text widgets for visibility - "plugs:" list replaced with "single_plugs:" and "hold_plugs:" for declarative clarity (generator doesn't use them, but documents intent) infra/zigbee2mqtt/configuration.yaml: - Removed DJ Booth from "all_plugs" group (now Squiggle, Sideboard Lamp, Bamboo Lights only) - Pushed same change to live /root/zigbee2mqtt/configuration.yaml and restarted zigbee2mqtt container Diagnostics in justfile + scripts/: justfile: - Constants: RPI_HOST, MQTT_HOST, MQTT_PORT, RPI_DIR, NR_PORT - rpi, mqtt-pub, mqtt-sub, mqtt-watch, mqtt-button - nr-show, nr-diff, nr-logs, nr-editor, nr-dashboard - z2m-devices, z2m-logs, z2m-state - mqtt-logs, restart, restart-all scripts/mqtt.py: - MQTT publish/subscribe/watch helpers using paho-mqtt v2.x - Run via uv run --with paho-mqtt (no global install needed) scripts/diag.py: - nr-show: live flow summary with mqtt-out topic list - nr-diff: structural diff deployed vs repo, normalizes random uuids - z2m-devices: paired devices + group membership - mqtt-button: publish action and watch /set topics (E2E test) Verified end-to-end via `just mqtt-button`: single → 3 messages on .../set (Squiggle, Sideboard Lamp, Bamboo Lights) release → 1 message on .../set (DJ Booth)
90 lines
2.5 KiB
Python
90 lines
2.5 KiB
Python
#!/usr/bin/env python3
|
|
"""MQTT diagnostic helpers — invoked by justfile mqtt-* recipes."""
|
|
import argparse
|
|
import sys
|
|
import time
|
|
|
|
import paho.mqtt.client as mqtt
|
|
|
|
|
|
def pub(host, port, topic, payload):
|
|
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
|
c.connect(host, port, 60)
|
|
c.loop_start()
|
|
info = c.publish(topic, payload)
|
|
info.wait_for_publish(timeout=5)
|
|
c.loop_stop()
|
|
c.disconnect()
|
|
|
|
|
|
def sub(host, port, topic, seconds):
|
|
got = []
|
|
|
|
def on_msg(client, userdata, msg):
|
|
got.append((msg.topic, msg.payload.decode()))
|
|
|
|
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
|
c.on_message = on_msg
|
|
c.connect(host, port, 60)
|
|
c.loop_start()
|
|
c.subscribe(topic)
|
|
end = time.monotonic() + seconds
|
|
while time.monotonic() < end:
|
|
time.sleep(0.1)
|
|
c.loop_stop()
|
|
c.disconnect()
|
|
for t, p in got:
|
|
print(t, p)
|
|
|
|
|
|
def watch(host, port, topic):
|
|
def on_msg(client, userdata, msg):
|
|
print(msg.topic, msg.payload.decode())
|
|
|
|
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
|
c.on_message = on_msg
|
|
c.connect(host, port, 60)
|
|
c.loop_start()
|
|
c.subscribe(topic)
|
|
try:
|
|
while True:
|
|
time.sleep(0.5)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
c.loop_stop()
|
|
c.disconnect()
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
sub_p = ap.add_subparsers(dest="cmd", required=True)
|
|
|
|
ppub = sub_p.add_parser("pub", help="Publish a single message")
|
|
ppub.add_argument("--host", default="192.168.81.147")
|
|
ppub.add_argument("--port", type=int, default=1883)
|
|
ppub.add_argument("--topic", required=True)
|
|
ppub.add_argument("--payload", required=True)
|
|
|
|
psub = sub_p.add_parser("sub", help="Subscribe for N seconds and print messages")
|
|
psub.add_argument("--host", default="192.168.81.147")
|
|
psub.add_argument("--port", type=int, default=1883)
|
|
psub.add_argument("--topic", required=True)
|
|
psub.add_argument("--seconds", type=float, default=5.0)
|
|
|
|
pwatch = sub_p.add_parser("watch", help="Subscribe forever and print messages")
|
|
pwatch.add_argument("--host", default="192.168.81.147")
|
|
pwatch.add_argument("--port", type=int, default=1883)
|
|
pwatch.add_argument("--topic", default="zigbee2mqtt/#")
|
|
|
|
args = ap.parse_args()
|
|
if args.cmd == "pub":
|
|
pub(args.host, args.port, args.topic, args.payload)
|
|
elif args.cmd == "sub":
|
|
sub(args.host, args.port, args.topic, args.seconds)
|
|
elif args.cmd == "watch":
|
|
watch(args.host, args.port, args.topic)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|