04 API Asystom Advisor
Source de vérité
Document : Lorawan_Network_Integration.pdf joint au mail de Patrick Duc (p.duc@external.asystom.com) « Documentation d'interface avec serveur Asystom Advisor » du 2026-04-24, en réunion conjointe avec Guillaume Ponton (IoT Valley).
Flux 02 — Uplink push
Endpoint
| URL | POST <asystom_url>/lorawan |
|---|---|
| Méthode | POST |
| Content-Type | application/json |
| Auth | Authorization: Basic <base64(user:password)> |
Headers obligatoires
| Header | Source canvas |
|---|---|
Authorization | 'Basic ' .. global.asystom_auth_b64 |
Content-Type | application/json |
X-Network | global.asystom_network (recommandation Patrick : passer le network identifier en header HTTP plutôt qu'en body) |
Body JSON (Loriot-like, validé Asystom)
La doc Asystom dit que le format de body dépend du LNS source (Actility, Kerlink, Loriot, Multitech). Patrick a fourni un exemple Loriot complet (voir doc PDF) que le déserializer Asystom comprend déjà. La chaîne canvas envoie un format Loriot-compatible :
{
"endDevice": {
"devEui": "0004A30B010593C1",
"devAddr": "00DA5139"
},
"fPort": 4,
"fCntUp": 1182,
"payload": "00A2070E4C210239360BE184F67DD87C417CC87A...",
"encodingType": "HEXA",
"recvTime": 1714056772000,
"client": "terega",
"dataRate": "SF7BW125",
"gwInfo": [
{
"rfRegion": "EU868",
"rssi": -85,
"snr": 10.5
}
]
}
Champs et leur source
| Champ | Source canvas |
|---|---|
endDevice.devEui | msg.DevEUI |
endDevice.devAddr | msg.DevAddr |
fPort | msg.fport |
fCntUp | msg.FCntUp |
payload | Hex uppercase de msg.payload |
encodingType | Hardcoded "HEXA" |
recvTime | os.time() * 1000 (epoch ms) |
client | global.asystom_client (Asystom client identifier, utilisé aussi pour le polling downlink) |
dataRate | Hardcoded "SF7BW125" pour l'instant (TODO : extraire du msg) |
gwInfo[0].rfRegion | Hardcoded "EU868" |
gwInfo[0].rssi | msg.rssi |
gwInfo[0].snr | msg.snr |
Codes de réponse (per spec PDF)
| Code | Statut | Comportement canvas |
|---|---|---|
| 201 | Frame processed | global.last_asystom_ok_ts = os.time() |
| 401 | Invalid credentials | Asystom = Basic auth (pas DAP). 401 = mauvais credentials → log + alerte. Ne déclenche pas le retry DAP. |
| 500 | Server Error | Mis en queue offline (queueenabled=true), replay auto. |
Flux 03 — Downlink polling
Endpoint
| URL | GET <asystom_url>/getPendingMsgs?client=<asystom_client> |
|---|---|
| Méthode | GET |
| Content-Type | application/x-www-form-urlencoded |
| Auth | Basic <asystom_auth_b64> |
Cadence
Inject cron asystom downlink poll avec repeat=60 (60s) et onceDelay=30. Donc 1 poll toutes les 60 secondes.
Réponse — format Asystom (per doc PDF)
{
"device_list": {
"0": "00-04-A3-0B-01-05-93-C1",
"1": "00-04-A3-0B-01-05-93-C2"
},
"payload": {
"0": "054114003C003C006801",
"1": "00"
},
"cmd": {
"0": "70",
"1": "64"
}
}
Si device_list est vide, aucun downlink à émettre. Sinon, pour chaque index i, la balise device_list[i] doit recevoir un downlink avec payload[i] sur FPort cmd[i].
Parsing canvas
Le node parse_asystom_dl extrait les 3 sections via regex Lua (le sandbox LuvitRED n'a pas json.decode garanti) :
local section_devices = body:match('"device_list"%s*:%s*(%b{})')
local section_payloads = body:match('"payload"%s*:%s*(%b{})')
local section_cmds = body:match('"cmd"%s*:%s*(%b{})')
-- Pour chaque section, extraire les paires "i":"value"
for k, v in section:gmatch('"([^"]+)"%s*:%s*"([^"]*)"') do t[k] = v end
-- Recompacter le DevEUI : "AA-BB-CC-DD-EE-FF-00-11" → "AABBCCDDEEFF0011"
local deveui = mac:gsub('-', ''):upper()
Émission du downlink LoRa
Pour chaque device, le parser construit :
msg.app = {
queue = {
[DevEUI] = {
data = payload_hex,
fport = tonumber(cmd_str),
confirmed = false,
},
},
}
Et l'émet sur output 0 (multi-msg). La sortie est wirée vers le node lora-app input qui queue le downlink. Au prochain uplink de la balise (Class A), le downlink est envoyé pendant la wait window.
FPort downlink connus
| FPort | Action Asystom | Format payload |
|---|---|---|
| 64 | Reboot balise | "00" ou TODO précis Asystom |
| 70 | Set scheduling frequency | 054114XXXXXXXX6801 avec X = secondes/10 little-endian |
Helper Lua main_flow.build_frequency_payload(seconds) et build_frequency_command(seconds) dans main_flow.lua pour générer ces payloads.
Globals à fournir
| Variable | Description | À fournir par |
|---|---|---|
global.asystom_url | URL base du serveur Asystom Advisor | Patrick Duc / Asystom |
global.asystom_auth_b64 | Base64 de user:password | Patrick Duc / Asystom |
global.asystom_network | Identifiant unique du réseau LoRaWAN (passé en header X-Network) | convenu avec Asystom (ex: cloudgate-terega-01) |
global.asystom_client | Asystom client identifier. Utilisé en body uplink ET en query param du polling DL. | Patrick Duc / Asystom (ex: terega) |