Finding device ID using script, then sending downlink, without widget

Hi,
I’m stuck at the point where I’m only able to send downlink to a specific Device ID (only if I declared the device id environment variable).

I want to know if there’s a way using script to get triggered device ID and send downlink to the specific ID without declaring any device_id environment variable. I’m currently using node.JS to try out different codes.

I’ve tried to modify the code for checkin status(to find out the device id using script) and sending downlink(send data to device id from envVar after triggered by variable). By using Action, I can run both scripts at the same time, but I want to have a script that is able to combine this two together, so that I can know which was the device ID that needed to send downlink. Is there anyone that can help me on this?

Hi @g.nzh,

There’s multiple ways to achieve that. One way is to declare tags in your device and fetch devices related to the wanted one;

Follow the example: (using tag key/value device_type temp_sensor)

  const device_list = await account.devices.list({
    page: 1,
    fields: ["id", "name", "tags", "created_at"],
    filter: { tags: [ { key: "device_type", value: "temp_sensor" }] },
    amount: 9999,
  });

then you can console log, or run externally and place breakpoints, so you can see what type of data is coming in.

I will attach into this also a link to our SDK which will be very helpful https://js.sdk.tago.io/

-Guilherme

If you are triggering your script using a widget or action (by variable), you can get it form the parameter “origin” of your variables.

The following function is very useful for that, and you can add to your code:

// This function helps us get the device using just its id.
async function getDevice(account, device_id) {
  const token = await Utils.getTokenByName(account, device_id);
  const dev = new Device({ token: token });
  return dev;
}
//
// Use as:
// const account = new Account({ token: "account-token" });
// const device = await getDevice(account, scope[0].origin);

This was also discussed in another post: operateData - device_token of device which received the last data - Analysis - TagoIO Community

Thanks @guilhermeco @vitor .

However, currently I’m fixing my ‘Sending downlink using dashboards’ to send downlink without dashboard and this is my script:

const { Analysis, Account, Utils } = require('@tago-io/sdk');
const axios        = require('axios');

async function init(context) {

  context.log('Downlink analysis started');
  // Get the environment variables.
  const env = Utils.envToJson(context.environment);

  const account = new Account({ token: env.account_token });
  const payload = { value: env.payload, origin: env.device_id };
  const port = { value: env.default_PORT };
  
  const device_id = payload.origin; // All variables that trigger the analysis have the "origin" parameter, with the TagoIO Device ID.
  
  // Find the token containing the authorization code used.
  const device_tokens = await account.devices.tokenList(device_id, { page: 1, fields: ['name', 'serie_number', 'last_authorization'], amount: 10 });
  const token = device_tokens.find(x => x.serie_number && x.last_authorization);
  if (!token) return context.log("Couldn't find a token with serial/authorization for this device");

  // Get the connector ID from the device
  const { network: network_id } = await account.devices.info(device_id);
  if (!network_id) return context.log('Device is not using a network.');

  // Get the network information with the NS URL for the Downlink
  const network = await account.integration.networks.info(network_id, ['id', 'middleware_endpoint', "name"]);
  if (!network.middleware_endpoint) return context.log("Couldn't find a network middleware for this device.");

  // Set the parameters for the device. Some NS like Everynet need this.
  const params = await account.devices.paramList(device_id);
  let downlink_param = params.find(x => x.key === 'downlink');
  downlink_param = { id: downlink_param ? downlink_param.id : null, key: 'downlink', value: String(payload.value), sent: false };
  await account.devices.paramSet(device_id, downlink_param);

  context.log('Trying to send the downlink');
  const data = {
    device: token.serie_number,
    authorization: token.last_authorization,
    payload: payload.value,
    port: port.value,
  };

  await axios.post(`https://${network.middleware_endpoint}/downlink`, data)
   .then((result) => {
     context.log(`Downlink accepted with status ${result.status}`);
   })
   .catch((error) => {
     context.log(`Downlink failed with with status ${error.response.status}`);
     context.log(error.response.data || JSON.stringify(error));
   });
}

module.exports = new Analysis(init);

Is there a way I can find the Device ID & Token without hard coding ‘device_id’ in the environment variables? As I have multiple devices so I’m trying to find out if I can get the device id from script and send the downlink, instead of hardcoding many ‘device_id’ to many analysis script.
I tried to change the above code to run without the device_id in the environment variables but I got this in the console:

[2021-07-08 10:43:50] {}
[2021-07-08 10:43:50] Downlink analysis started
[2021-07-08 10:43:49] Starting analysis 60d***************

Was wondering what modification need to be done to this script so I’m able to find out which Device triggered the Action to run this downlink Analysis to Loriot.

I am very clueless now as I don’t really know much about Loriot Node.JS :sweat_smile:
I would very much appreciate your help in this!

1 Like

From your description, you did setup an Action By Variable to run this is script. Is that correct?

If that’s so, you need to edit the:
async function init(context) to async function init(context, scope)
Because any data regarding what triggered your analysis is available in the scope parameter of the analysis.

When you receive information in the scope, usually the information regarding which device send the data will be available in the scope[0].origin. That said, you can just edit the following line to:

const payload = { value: env.payload, origin: env.device_id };

// to 

const payload = { value: env.payload, origin: scope[0].origin };

Yes it worked! Thanks for the explanation!