RE: Implementing an Analysis to notify when a device is inside of a geofence

Hi Filipe

Thank you for responding to my query. But again the advice you give does not get to the core of my problem. I have successfully created a analysis that checks if device A is within a geofence and sends a notification if it is not. I have successfully created a analysis if device A is is with in multiple geofences with notifications as well.

This has been possible because in the map widget i am able to add blue print devices with a location variable. This allows the map widget to display many device on the map.
add device

I am also able to add a geofence variable for only one device. This variable can store up to 10 geofences. Let me reinforce this statement, this geofence variable is only for device A , this means that device B does not have a geofence variable.
add geofence1

As such i am unable to write analytics to check if device b is within the geogence.

Let me try to explain it another way.

When device A updates the bucket variables due to TTN update it updates the location variable and the tagoIO action is triggered which runs an analysis to check if device A is with in the geofence which is stored in the geofence variable on the bucket which gets updated from the map widget when the user edits it.
When device B updates the bucket variable due to TTN update it updates the location variable and the tagoIO action is triggered which runs an analysis to check if device B is with in the geofence which should be in the geofence variable but there isn’t one.

Maybe i don’t understand this correctly. Please help me to understand.

Thanks
Flaxie

The thing is that you don’t need to store the geofence for each device inside of it, you can store the geofences for both devices A and B in the same place (A new device or maybe the device B could use the same geofence variable from device A).

Look at this example

I receive notification for both devices SENSOR 1 and SENSOR 2 when it is inside or outside a geofence, and I use the variable geofences from the device SENSOR 1 to keep the geofences stored

Captura de tela de 2021-03-18 12-03-02

Captura de tela de 2021-03-18 12-04-31

What I did to achieve the example above was following the steps from the previous post, but adapt the Analysis and the Action trigger for Multiple devices.

And here is my new Analysis, you can use that, just change the geofence store device id, and the variables name.

/*
 * Analysis Example
 * Geofences
 *
 * Use geofences to control the area that your devices are in.
 *
 * Instructions
 * To run this analysis you need to add an account token to the environment variables,
 * To do that, go to your account settings, then token and copy your token.
 * 1 - Enter the following link: https://admin.tago.io/account/
 * 2 - Select your Profile.
 * 3 - Enter Tokens tab.
 * 4 - Generate a new Token with Expires Never.
 * 5 - Press the Copy Button and place it at the Environment Variables tab of this analysis with key account_token.
 *
 * Follow this guide https://docs.tago.io/en/articles/151 and create
 * two geofences, one with the event code 'danger' and another named 'safe'.
 */

const { Utils, Account, Analysis, Device, Services } = require("@tago-io/sdk");
const geolib = require("geolib");

// This function checks if our device is inside a polygon geofence
function insidePolygon(point, geofence) {
  const x = point[1];
  const y = point[0];
  let inside = false;
  for (let i = 0, j = geofence.length - 1; i < geofence.length; j = i++) {
    const xi = geofence[i][0];
    const yi = geofence[i][1];
    const xj = geofence[j][0];
    const yj = geofence[j][1];
    const intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
    if (intersect) inside = !inside;
  }
  return inside;
}

// This function checks if our device is inside any geofence
function checkZones(point, geofence_list) {
  // The line below gets all Polygon geofences that we may have.
  const polygons = geofence_list.filter((x) => x.geolocation.type === "Polygon");
  if (polygons.length) {
    // Here we check if our device is inside any Polygon geofence using our function above.
    const pass_check = polygons.map((x) => insidePolygon(point, x.geolocation.coordinates[0]));
    const index = pass_check.findIndex((x) => x === true);
    if (index !== -1) return polygons[index];
  }
  // The line below gets all Point (circle) geofences that we may have.
  const circles = geofence_list.filter((x) => x.geolocation.type === "Point");
  if (circles.length) {
    // Here we check if our device is inside any Point geofence using a third party library called geolib.
    const pass_check = circles.map((x) =>
      geolib.isPointWithinRadius(
        { latitude: point[1], longitude: point[0] },
        { latitude: x.geolocation.coordinates[0], longitude: x.geolocation.coordinates[1] },
        x.geolocation.radius
      )
    );
    const index = pass_check.findIndex((x) => x);
    if (index !== -1) return circles[index];
  }
  return;
}

// This function help us get the device using just its id.
async function getDevice(account, device_id) {
  const customer_token = await Utils.getTokenByName(account, device_id);
  const customer_dev = new Device({ token: customer_token });
  return customer_dev;
}

async function startAnalysis(context, scope) {
  context.log("Running");

  if (!scope[0]) throw "Scope is missing"; // doesn't need to run if scope[0] is null

  // The code block below gets all environment variables and checks if we have the needed ones.
  const environment = Utils.envToJson(context.environment);
  if (!environment.account_token) throw "Missing account_token environment var";

  // The line below starts our notification service.
  const notification = new Services({ token: context.token }).Notification;

  const account = new Account({ token: environment.account_token });
  const device_id = scope[0].origin;

  // Here we get the device information using our account data and the device id.
  const device = await getDevice(account, device_id);

  const deviceInfo = await device.info();

  // This checks if we received a location
  const location = scope.find((data) => data.variable === "location");
  if (!location || !location.location) return context.log("No location found in the scope.");

  // The geofence device that will keep the geofence coordinates data
  const geofenceDevice = await getDevice(account, "GEOFENCE DEVICE ID");
  const geofenceDeviceInfo = await geofenceDevice.info();
  context.log(geofenceDeviceInfo.name, deviceInfo.name);
  // Now we check if we have any geofences to go through.
  const geofences = await geofenceDevice.getData({ variable: "GEOFENCE VARIABLE NAME", qty: 10 });
  const zones = geofences.map((geofence) => geofence.metadata);

  const zone = checkZones(location.location.coordinates, zones);

  if (zone) {
    notification.send({ title: `${deviceInfo.name} NOTIFICATION`, message: `Your device ${deviceInfo.name} is INSIDE of geofence.` });
    return;
  }

  // If no geofence is found, we stop our application sending a notification.
  notification.send({ title: `${deviceInfo.name} NOTIFICATION`, message: `Your device ${deviceInfo.name} is OUTSIDE of geofence.`});
}

module.exports = new Analysis(startAnalysis);

Hi Filipe

Thanks for getting back to me so quickly.
I think i understand now and will go through your analysis in more detail.
One last question to help me further understand. With regards your statement

“I receive notification for both devices SENSOR 1 and SENSOR 2 when it is inside or outside a geofence, and I use the variable geofences from the device SENSOR 1 to keep the geofences stored”

If device SENSOR 1 is removed from service will your analysis stop functioning until you edit the map widget settings and save the geofence in another device for instance SENSOR 2.

Hope my question makes sense.

Kind Regards
Flaxie :grinning:

If I clean the SENSOR 1 Device and Bucket data from the system, my map widget will not show any geofence because it is empty, so it will be necessary to change it at the widget.

But I could create a device to manage that geofence storage, a new one, that is not related to any hardware to keep this kind of data, so if I disable the SENSOR1, SENSOR2, SENSOR3… my geofence will keep running and the Geofence Device ID that I used at my Analysis will be easier to keep.

Hi @flaxies,

I’m going to convert all this messages in a public post, so other people can contribute ok :wink:

Hi Alinetusi

That is quite ok with me.

Good idea thank you