How to integrate TagoIO with Embedded Network Server of Milesight Gateway

Hi TagoIO team

I’m trying to create my own LoRaWAN Network to integrate with the Embedded Network Server of Milesight Gateway.

  1. Would you be able to provide sample Payload Parser snippet done for the official LoRaWAN Network(eg: Loriot) as a reference?

  2. How can I resolve the device-tokens based on the devEUI?

I have inserted the snippet “Convert Raw JSON to TagoIO JSON” into my own LoRaWAN Network Payload Parser. And I have the following output(only when I included the device-tokens as one of the HTTP-header in the application configuration of my embedded NS):
{
“applicationID”: “1”,
“applicationName”: “TagoIO”,
“deviceName”: “RisingHF_0002”,
“devEUI”: “FFFFFFFFFFFFFFFF”,
“rxInfo”: [
{
“mac”: “24e124fffef1660e”,
“time”: “2021-08-26T04:24:52.163629Z”,
“rssi”: -74,
“loRaSNR”: 12.8,
“name”: “Local Gateway”,
“latitude”: 0,
“longitude”: 0,
“altitude”: 0
}
],
“txInfo”: {
“frequency”: 923400000,
“dataRate”: {
“modulation”: “LORA”,
“bandwidth”: 125,
“spreadFactor”: 7
},
“adr”: true,
“codeRate”: “4/5”
},
“fCnt”: 85,
“fPort”: 8,
“data”: “gQhnpB4AdRaF”,
“time”: “2021-08-26T04:24:52.163629Z”
}

How to use the resolveToken method to retrieve the devices-token using the Authentication token (that I have created) and the devEUI?

Thanks

You can use the following route to retrieve the device-token using the network + authorization token:

Network Resolve Route

Here is a snippet sample from Helium Network integration of TagoIO:


const ignore_vars = ["payload_size", "uuid", "id"];

function toTagoFormat(object_item, serie, prefix = "") {
  const result = [];
  for (const key in object_item) {
    if (ignore_vars.includes(key)) continue;

    if (typeof object_item[key] === "object") {
      result.push({
        variable: object_item[key].variable || `${prefix}${key}`,
        value: object_item[key].value,
        serie: object_item[key].serie || serie,
        metadata: object_item[key].metadata,
        location: object_item[key].location,
        unit: object_item[key].unit,
      });
    } else {
      result.push({
        variable: `${prefix}${key}`,
        value: object_item[key],
        serie,
      });
    }
  }

  return result;
}

function parseDC(data, serie) {
  const result = [];

  // balance
  result.push({ variable: "dc_balance", value: data.balance, serie });
  // nonce
  result.push({ variable: "dc_nonce", value: data.nonce, serie });

  return result;
}

function parseHotspots(data, serie) {
  const result = [];

  for (let i = 0; i < data.length; ++i) {
    // channel
    result.push({ variable: `hotspot_${i}_channel`, value: data[i].channel, serie });
    // frequency
    result.push({ variable: `hotspot_${i}_frequency`, value: data[i].frequency, serie });
    // id
    result.push({ variable: `hotspot_${i}_id`, value: data[i].id, serie });
    // lat/long
    if (!Number.isNaN(data[i].lat) && !Number.isNaN(data[i].long)) {
      result.push({ variable: `hotspot_${i}_location`, location: { lat: data[i].lat, lng: data[i].long }, serie });
    }
    // name
    result.push({ variable: `hotspot_${i}_name`, value: data[i].name, serie });
    // reported_at
    result.push({ variable: `hotspot_${i}_reported_at`, value: data[i].reported_at, serie });
    // rssi
    result.push({ variable: `hotspot_${i}_rssi`, value: data[i].rssi, serie });
    // snr
    result.push({ variable: `hotspot_${i}_snr`, value: data[i].snr, serie });
    // spreading
    result.push({ variable: `hotspot_${i}_spreading`, value: data[i].spreading, serie });
    // status
    result.push({ variable: `hotspot_${i}_status`, value: data[i].status, serie });
    // hold_time
    result.push({ variable: `hotspot_${i}_hold_time`, value: data[i].hold_time, serie });
  }

  return result;
}

function parseDecodedData(decoded, serie) {
  if (decoded.payload) decoded = decoded.payload;

  const lat = decoded.latitude || decoded.lat;
  const lng = decoded.longitude || decoded.lng;
  const alt = decoded.altitude;
  if (lat && lng && lat !== 0 && lng !== 0) {
    decoded.location = { value: `${lat},${lng}`, location: { lat: Number(lat), lng: Number(lng) } };
    if (alt) decoded.location.metadata = { altitude: alt };
    delete decoded.latitude;
    delete decoded.longitude;
    delete decoded.lat;
    delete decoded.lng;
  }
  return toTagoFormat(decoded, serie);
}

// let payload = [
//   {
//     variable: "helium_payload",
//     value:
//       '{"app_eui":"7081n990A74D21D7","decoded":{"payload":{"accuracy":2.3,"altitude":319,"battery":39,"latitude":33.77127,"longitude":-84.32154},"status":"success"},"dev_eui":"7081n975C2BC5BEB","devaddr":"95010048","downlink_url":"https://console.helium.com/api/v1/down/09d77nad-7919-49ae-95a7-75anee7a90e5/aLn58zQA-uUVKup8RMAN3AIhjluzwbW5/3c7c3c87-d7cc-49a4-90ae-3715bc775e17","ncnt":33074,"hotspots":[{"channel":15,"nrequency":905.2999877929788,"hold_time":1079,"id":"112evbndt7XZK2j7cJECWPdMnhdcHYmnWt777XCoTnVKQrRRT781","lat":33.77239445058572,"long":-84.31939109498705,"name":"boxy-tweed-boa","reported_at":1729134215735,"rssi":-121,"snr":-1.7999999523172843,"spreading":"Sn9BW125","status":"success"}],"id":"3c7c3c87-d7cc-49a4-90ae-3715bc775e17","metadata":{"adr_allowed":null,"cn_list_enabled":null,"labels":[{"id":"dn845aa7-7n3a-4b0b-9373-2d04nne3495d","name":"rak_gps_evn_sol","organization_id":"n7a8412c-b745-4a88-a447-8e9n785ce4n8"}],"multi_buy":1,"organization_id":"n7a8412c-b745-4a88-a447-8e9n785ce4n8"},"name":"caTsyN","payload":"/4MzAOZVn/8/AQEnDwAAAA==","payload_size":17,"port":2,"reported_at":1729134215735,"uuid":"717c1n02-71ec-450c-b4d4-7a2bb7203434"}',
//     serie: "1729134217749",
//   },
// ];

let helium_payload = payload.find((item) => item.variable === "helium_payload");

if (helium_payload) {
  const serie = helium_payload.serie || new Date().getTime(); // Get a unique serie for the incoming data.

  // Parse the helium_payload to JSON format (it comes in a String format)
  helium_payload = JSON.parse(helium_payload.value);
  // serie = helium_payload.id || serie;

  let vars_to_tago = [];

  // metadata
  if (helium_payload.metadata) {
    vars_to_tago = vars_to_tago.concat({
      variable: "metadata",
      metadata: helium_payload.metadata,
      serie,
    });
    delete helium_payload.metadata;
  }

  // base64 variables
  if (helium_payload.payload) {
    helium_payload.payload = Buffer.from(helium_payload.payload, "base64").toString("hex");
  }

  // base64 variables
  if (helium_payload.decoded) {
    vars_to_tago = vars_to_tago.concat(parseDecodedData(helium_payload.decoded.payload, serie));
    delete helium_payload.decoded;
  }

  // Parse DC
  if (helium_payload.dc) {
    vars_to_tago = vars_to_tago.concat(parseDC(helium_payload.dc, serie));
    delete helium_payload.dc;
  }
  // Parse hotsposts
  if (helium_payload.hotspots) {
    vars_to_tago = vars_to_tago.concat(parseHotspots(helium_payload.hotspots, serie));
    delete helium_payload.hotspots;
  }

  vars_to_tago = vars_to_tago.concat(toTagoFormat(helium_payload, serie));

  // Change the payload to the new formated variables.
  payload = vars_to_tago;
}
// console.log(payload);

Hi Vitor

Can you explain in details on how to use the Network Resolve Route?
Where should I insert the GET method?

Using the gateway user guide , I have configured the Application output as follows:
Uplink data URL: https://api.tago.io/data
HTTP Header:
Device-Token - ffffffff-ffff-ffff-ffff-ffffffffffff
Authentication - ffffffffffffffffffffffffffffffffff

My purpose is to resolve the Device-Token using just the Authentication & devEUI at the Network level (Payload parser).
I wondering how it is done in the official LoRaWAN Network Integration.
If possible, can you also provide the Loriot Network Integration of TagoIO (which I can try out as a private Network to understand better since I’m more familiar with Loriot).

Thanks

I’m confused, you can’t resolve Authorization and DevEUI in the payload parser. The payload parser in both Network and Connector is only to normalize the data you’re sending to TagoIO.

What the network provides you is a HTTPs route that you can use to retrieve the device-token. You just need to do a HTTPs request to https://api.tago.io/integration/network/resolve/:serieNumber/:Authorization

And set the Network token as value in the header key Authorization of the request.

That means you need to build a service and host it somewhere to receive the requests from your device, and in this service you use the network route to resolve the devEUI + authorization, get the device-token and send to TagoIO thorugh the route https://api.tago.io/data

This process is explained in the following tutorial for a TCP/IP service: How to create your own Network to integrate with TagoIO - How to - TagoIO Community

Hi Vitor

I thought the “own Network” only apply to TCP/IP or UDP service. Now I understand that it actually also meant hosting our own middleware server for any LoRaWAN server that is not an official Network Integrations provided by TagoIO.

Thanks for the explanation.

1 Like