04 API Asystom Advisor

Format aligné spec PDF Patrick Duc (Asystom) du 2026-04-24 · Flux 02 + 03

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

URLPOST <asystom_url>/lorawan
MéthodePOST
Content-Typeapplication/json
AuthAuthorization: Basic <base64(user:password)>

Headers obligatoires

HeaderSource canvas
Authorization'Basic ' .. global.asystom_auth_b64
Content-Typeapplication/json
X-Networkglobal.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

ChampSource canvas
endDevice.devEuimsg.DevEUI
endDevice.devAddrmsg.DevAddr
fPortmsg.fport
fCntUpmsg.FCntUp
payloadHex uppercase de msg.payload
encodingTypeHardcoded "HEXA"
recvTimeos.time() * 1000 (epoch ms)
clientglobal.asystom_client (Asystom client identifier, utilisé aussi pour le polling downlink)
dataRateHardcoded "SF7BW125" pour l'instant (TODO : extraire du msg)
gwInfo[0].rfRegionHardcoded "EU868"
gwInfo[0].rssimsg.rssi
gwInfo[0].snrmsg.snr

Codes de réponse (per spec PDF)

CodeStatutComportement canvas
201Frame processedglobal.last_asystom_ok_ts = os.time()
401Invalid credentialsAsystom = Basic auth (pas DAP). 401 = mauvais credentials → log + alerte. Ne déclenche pas le retry DAP.
500Server ErrorMis en queue offline (queueenabled=true), replay auto.

Flux 03 — Downlink polling

Endpoint

URLGET <asystom_url>/getPendingMsgs?client=<asystom_client>
MéthodeGET
Content-Typeapplication/x-www-form-urlencoded
AuthBasic <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

FPortAction AsystomFormat payload
64Reboot balise"00" ou TODO précis Asystom
70Set scheduling frequency054114XXXXXXXX6801 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

VariableDescriptionÀ fournir par
global.asystom_urlURL base du serveur Asystom AdvisorPatrick Duc / Asystom
global.asystom_auth_b64Base64 de user:passwordPatrick Duc / Asystom
global.asystom_networkIdentifiant unique du réseau LoRaWAN (passé en header X-Network)convenu avec Asystom (ex: cloudgate-terega-01)
global.asystom_clientAsystom client identifier. Utilisé en body uplink ET en query param du polling DL.Patrick Duc / Asystom (ex: terega)