90927ccc0e
Polls ntfy.sh for ALERT_klubhaus_topic(_test) and publishes
{state: TOGGLE} to zigbee2mqtt/Sideboard Lamp/set on each new
message event. Dual-topic subscription so switching from test to
production requires no code change; sender still uses test topic.
- infra/doorbell-listener/: uv-based Docker service (arm64)
- compose: depends_on mosquitto, persists last_id per topic
- justfile: doorbell-publish, doorbell-logs, doorbell-rebuild,
restart-all now includes doorbell-listener
178 lines
6.0 KiB
Makefile
178 lines
6.0 KiB
Makefile
# nr-flow-validator — Declarative Node-RED flows with conftest validation
|
|
|
|
set export
|
|
|
|
# Default: generate a flow, validate, and show results
|
|
default: validate
|
|
|
|
# ---- Flow generation ----
|
|
|
|
# Generate Node-RED flow JSON from a YAML spec
|
|
generate FLOW="zigbee-monitor":
|
|
@echo "=== Generating {{FLOW}} ==="
|
|
python3 scripts/flow2json.py flows/{{FLOW}}.yaml > output/{{FLOW}}.json
|
|
@echo "Wrote output/{{FLOW}}.json"
|
|
|
|
# Generate all flows
|
|
generate-all:
|
|
@for f in flows/*.yaml; do \
|
|
name=$(basename "$f" .yaml); \
|
|
just generate "$name"; \
|
|
done
|
|
|
|
# ---- Validation ----
|
|
|
|
# Validate a generated flow against Rego policies
|
|
validate FLOW="zigbee-monitor": generate
|
|
@echo "=== Validating {{FLOW}} ==="
|
|
conftest test output/{{FLOW}}.json -p policy/ --namespace nodered --no-color
|
|
|
|
# Validate all flows
|
|
validate-all:
|
|
@for f in output/*.json; do \
|
|
echo "=== Validating $(basename "$f") ==="; \
|
|
conftest test "$$f" -p policy/ --namespace nodered --no-color; \
|
|
done
|
|
|
|
# ---- Deployment ----
|
|
|
|
# Deploy flow to Node-RED instance
|
|
deploy FLOW="zigbee-monitor" HOST="http://192.168.81.147:1880": generate validate
|
|
@echo "=== Deploying {{FLOW}} to {{HOST}} ==="
|
|
curl -s -o /dev/null -w "HTTP %{http_code}" \
|
|
-X POST {{HOST}}/flows \
|
|
-H "Content-Type: application/json" \
|
|
-H "Node-RED-Deployment-Type: full" \
|
|
-d @output/{{FLOW}}.json
|
|
@echo ""
|
|
|
|
# Quick deploy (skip validation)
|
|
deploy-quick FLOW="zigbee-monitor" HOST="http://192.168.81.147:1880": generate
|
|
@echo "=== Quick deploy {{FLOW}} to {{HOST}} ==="
|
|
curl -s -o /dev/null -w "HTTP %{http_code}" \
|
|
-X POST {{HOST}}/flows \
|
|
-H "Content-Type: application/json" \
|
|
-H "Node-RED-Deployment-Type: full" \
|
|
-d @output/{{FLOW}}.json
|
|
@echo ""
|
|
|
|
# ---- Viewing ----
|
|
|
|
# Fetch and display current flows from Node-RED
|
|
pull HOST="http://192.168.81.147:1880":
|
|
curl -s {{HOST}}/flows | python3 -m json.tool
|
|
|
|
# Open dashboard in browser
|
|
dashboard:
|
|
xdg-open http://192.168.81.147:1880/dashboard 2>/dev/null || open http://192.168.81.147:1880/dashboard 2>/dev/null || echo "Open: http://192.168.81.147:1880/dashboard"
|
|
|
|
# ---- Development ----
|
|
|
|
# Watch for changes and re-validate
|
|
watch FLOW="zigbee-monitor":
|
|
@while true; do \
|
|
inotifywait -q -e modify flows/{{FLOW}}.yaml policy/*.rego; \
|
|
clear; just validate {{FLOW}}; \
|
|
done
|
|
|
|
# Create a new flow from template
|
|
new NAME:
|
|
@cp flows/zigbee-monitor.yaml flows/{{NAME}}.yaml
|
|
@echo "Created flows/{{NAME}}.yaml — edit and run 'just validate {{NAME}}'"
|
|
|
|
# ---- Diagnostics ----
|
|
# All commands target the live RPi at $RPI_HOST (default root@192.168.81.147).
|
|
# Override with: just RPI_HOST=user@host mqtt-pub topic msg
|
|
|
|
RPI_HOST := "root@192.168.81.147"
|
|
MQTT_HOST := "192.168.81.147"
|
|
MQTT_PORT := "1883"
|
|
RPI_DIR := "/root"
|
|
NR_PORT := "1880"
|
|
|
|
# Run a command on the RPi over SSH
|
|
rpi CMD *ARGS:
|
|
ssh -o ConnectTimeout=5 {{RPI_HOST}} '{{CMD}} {{ARGS}}'
|
|
|
|
# Publish to MQTT broker on the RPi
|
|
mqtt-pub TOPIC PAYLOAD:
|
|
uv run --with paho-mqtt python3 scripts/mqtt.py pub --topic '{{TOPIC}}' --payload '{{PAYLOAD}}'
|
|
|
|
# Subscribe to MQTT broker on the RPi for N seconds
|
|
mqtt-sub TOPIC SECONDS="5":
|
|
uv run --with paho-mqtt python3 scripts/mqtt.py sub --topic '{{TOPIC}}' --seconds {{SECONDS}}
|
|
|
|
# Publish a button action and watch what /set topics fire (E2E test)
|
|
mqtt-button ACTION="single":
|
|
uv run --with paho-mqtt python3 scripts/diag.py mqtt-button --action {{ACTION}}
|
|
|
|
# Show live Node-RED flow summary (node count, function names, button wires, mqtt-out topics)
|
|
nr-show:
|
|
python3 scripts/diag.py show
|
|
|
|
# Diff deployed Node-RED flow against repo (excluding random uuid ids)
|
|
nr-diff FLOW="zigbee-monitor": generate
|
|
python3 scripts/diag.py diff output/{{FLOW}}.json
|
|
|
|
# Tail Node-RED container logs
|
|
nr-logs:
|
|
ssh {{RPI_HOST}} 'docker logs --tail 50 -f nodered'
|
|
|
|
# Tail zigbee2mqtt container logs
|
|
z2m-logs:
|
|
ssh {{RPI_HOST}} 'docker logs --tail 50 -f zigbee2mqtt'
|
|
|
|
# Tail mosquitto container logs
|
|
mqtt-logs:
|
|
ssh {{RPI_HOST}} 'docker logs --tail 50 -f mosquitto'
|
|
|
|
# Restart a service container
|
|
restart SERVICE:
|
|
ssh {{RPI_HOST}} 'docker restart {{SERVICE}}'
|
|
|
|
# Restart the full stack
|
|
restart-all:
|
|
ssh {{RPI_HOST}} 'docker restart mosquitto zigbee2mqtt nodered doorbell-listener'
|
|
|
|
# Tail doorbell-listener container logs
|
|
doorbell-logs:
|
|
ssh {{RPI_HOST}} 'docker logs --tail 50 -f doorbell-listener'
|
|
|
|
# Restart just the doorbell-listener
|
|
restart-doorbell:
|
|
ssh {{RPI_HOST}} 'docker restart doorbell-listener'
|
|
|
|
# Publish a test alert to ntfy (used for E2E testing the listener).
|
|
# Usage: just doorbell-publish "Title" "Body"
|
|
doorbell-publish TITLE BODY="Someone is at the door":
|
|
curl -sS -X POST "https://ntfy.sh/ALERT_klubhaus_topic_test" \
|
|
-H "Title: {{TITLE}}" \
|
|
-H "Priority: high" \
|
|
-d "{{BODY}}" \
|
|
-w "\nHTTP %{http_code}\n"
|
|
|
|
# Build + (re)start the listener (after edits to infra/doorbell-listener/)
|
|
doorbell-rebuild:
|
|
ssh {{RPI_HOST}} 'cd {{RPI_DIR}}/nr-flow-validator && docker compose build doorbell-listener && docker compose up -d doorbell-listener'
|
|
|
|
# Show paired zigbee devices and their friendly names
|
|
z2m-devices:
|
|
ssh {{RPI_HOST}} 'cat {{RPI_DIR}}/zigbee2mqtt/configuration.yaml' > /tmp/z2m.yaml
|
|
python3 scripts/diag.py z2m-devices /tmp/z2m.yaml
|
|
|
|
# Show per-device persisted state from z2m
|
|
z2m-state:
|
|
ssh {{RPI_HOST}} 'cat {{RPI_DIR}}/zigbee2mqtt/state.json' | python3 -m json.tool | head -60
|
|
|
|
# Open the Node-RED editor
|
|
nr-editor:
|
|
xdg-open http://{{MQTT_HOST}}:{{NR_PORT}} 2>/dev/null || open http://{{MQTT_HOST}}:{{NR_PORT}} 2>/dev/null || echo "Open: http://{{MQTT_HOST}}:{{NR_PORT}}"
|
|
|
|
# Open the Dashboard 2.0 SPA
|
|
nr-dashboard:
|
|
xdg-open http://{{MQTT_HOST}}:{{NR_PORT}}/dashboard 2>/dev/null || open http://{{MQTT_HOST}}:{{NR_PORT}}/dashboard 2>/dev/null || echo "Open: http://{{MQTT_HOST}}:{{NR_PORT}}/dashboard"
|
|
|
|
# Watch live MQTT traffic (matches the z2m base_topic by default)
|
|
mqtt-watch TOPIC="zigbee2mqtt/#":
|
|
uv run --with paho-mqtt python3 scripts/mqtt.py watch --topic '{{TOPIC}}'
|