From 2e244152246c46a0c7d1669f3ed0fddcc434695f Mon Sep 17 00:00:00 2001 From: gitomarcourt Date: Fri, 3 Jul 2026 17:58:05 +0200 Subject: [PATCH] docs: precise SNEK callback config for JSON POST body - Step-by-step field-by-field callback config table - Common pitfall documented: don't paste JSON body in "Line pattern" - Exact JSON body template format (spaces around colons required) - Companion dashboard deployment steps Backend also cleaned up: unified query-string / JSON body parsing via a single _num() helper instead of nested try/except blocks. Co-Authored-By: Claude Opus 4.7 --- README.md | 72 ++++++++++++++++++++++++++++++++---------------- dashboard/app.py | 29 +++++++++++-------- 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index b03635a..cd0ed1c 100644 --- a/README.md +++ b/README.md @@ -237,39 +237,65 @@ C'est là que ça devient puissant : à chaque message reçu, SNEK peut faire un ### Configuration -Dans la UI SNEK → onglet **Callbacks** → **Add callback** : -- **URL** : `http://mon-service.mon-ns.svc.cluster.local:3000/api/sigfox` -- **Method** : POST -- **Content-Type** : `application/json` -- **Body** : template avec variables SNEK (voir doc SNEK) +### Configurer un callback dans la UI SNEK -### Exemple de body template +Dans SNEK → onglet **Callbacks** → **New** (dans la section DATA callbacks) : + +| Champ | Valeur | +|-------|--------| +| **Type** | `UPLINK` | +| **Channel** | `URL` | +| **Send duplicate** | décoché | +| **Url pattern** | ton endpoint (ex: `http://mon-service.mon-ns.svc.cluster.local/webhook`) | +| **Line pattern** | *(vide)* | +| **Content type** | `application/json` | +| **Method** *(apparaît selon channel)* | `POST` | +| **Body** *(apparaît une fois Method=POST + Content-Type=application/json)* | template JSON ci-dessous | + +Body template (format exact — respecter les espaces autour des `:`) : ```json { - "device": "{device}", - "time": "{time}", - "data": "{data}", - "rssi": {rssi}, - "snr": {snr}, - "seqNumber": {seqNumber} + "device" : "{device}", + "data" : "{data}", + "time" : {time}, + "rssi" : {rssi}, + "snr" : {snr}, + "seqNumber" : {seqNumber} } ``` -Variables disponibles : -- `{device}` : ID hex du device -- `{time}` : timestamp Unix -- `{data}` : payload en hex -- `{rssi}` : force du signal reçu -- `{snr}` : rapport signal/bruit -- `{seqNumber}` : compteur de séquence +⚠️ **Piège classique** : ne pas coller le JSON dans **Line pattern**. Ce champ fait une substitution char-par-char et refuse tout ce qui n'est pas un nom de variable connu (tu obtiens `Wrong values. Please fix: ...`). Le body JSON va dans le champ **Body** qui n'apparaît qu'après avoir choisi Method=POST et Content-Type=application/json. + +### Variables disponibles + +`{device}` `{time}` `{data}` `{rssi}` `{snr}` `{seqNumber}` `{duplicate}` `{station}` `{avgSnr}` `{LQI}` ### Cas d'usage -- **Ingester dans une base** : POST vers une API Node/Go/Python qui insert dans Postgres/InfluxDB -- **Alertes** : POST vers Telegram bot API pour notification instantanée -- **Bus de messages** : POST vers Kafka REST proxy, RabbitMQ HTTP plugin, etc. -- **Dashboard live** : POST vers un WebSocket relay (Socket.io, Server-Sent Events) +- **Dashboard live** : dossier [`dashboard/`](./dashboard/) — Flask + SQLite + SSE + Chart.js prêt à l'emploi +- Ingérer dans une base (Postgres, InfluxDB via une API custom) +- Alertes Telegram / Slack / Discord +- Bus de messages Kafka / RabbitMQ + +### Dashboard intégré (companion service) + +Un mini dashboard responsive est fourni dans [`dashboard/`](./dashboard/) — il reçoit les callbacks SNEK, décode les 6 modes Sens'it (Standby, Temperature, Light, Door, Vibration, Magnet), stocke en SQLite et affiche en temps réel via Server-Sent Events. Tailwind + Chart.js. + +Pour le déployer : + +```bash +cd dashboard +docker build --platform linux/amd64 -t 192.168.1.100:30500/snek-dashboard:latest . +docker push 192.168.1.100:30500/snek-dashboard:latest +kubectl apply -f ../deploy/dashboard.yaml +``` + +Puis configure le callback SNEK avec l'URL : +``` +http://snek-dashboard.snek.svc.cluster.local/webhook +``` +et le body JSON template ci-dessus. Ouvre `http://` dans un navigateur. --- diff --git a/dashboard/app.py b/dashboard/app.py index 86064a7..6e1a0be 100644 --- a/dashboard/app.py +++ b/dashboard/app.py @@ -63,19 +63,24 @@ def index(): return send_from_directory("static", "index.html") -@app.route("/webhook", methods=["POST"]) +def _num(x, cast=float): + try: + return cast(x) + except (TypeError, ValueError): + return None + + +@app.route("/webhook", methods=["POST", "GET"]) def webhook(): - """ - SNEK callback endpoint. - SNEK sends JSON with: device, time, data, rssi, snr, seqNumber, etc. - """ - body = request.get_json(force=True, silent=True) or {} - device = (body.get("device") or "").lower() - raw = (body.get("data") or "").lower() - ts = float(body.get("time") or time.time()) - rssi = body.get("rssi") - snr = body.get("snr") - seq_number = body.get("seqNumber") + """SNEK callback endpoint. Accepts JSON body OR query string / form params.""" + src = request.get_json(force=True, silent=True) or request.values + + device = (src.get("device") or "").lower() + raw = (src.get("data") or "").lower() + ts = _num(src.get("time")) or time.time() + rssi = _num(src.get("rssi")) + snr = _num(src.get("snr")) + seq_number = _num(src.get("seqNumber") or src.get("seqnumber"), int) if not device or not raw: return jsonify({"error": "missing device or data"}), 400