geofence
Version:
Geofencing for npm
229 lines (207 loc) • 6.45 kB
JavaScript
/**
* Should be run inside [Termux](https://termux.com/) in Android using GPS
* First install the following apps:
* - https://play.google.com/store/apps/details?id=com.termux
* - https://play.google.com/store/apps/details?id=com.termux.api
*/
var defines = require("../defines");
const mri = require("mri");
const api = require("termux");
var utility_functions = require("../utility");
const util = require("util");
const prompt = require("prompt");
const promptGet = util.promisify(prompt.get);
var log = defines.log;
// if (!api.hasTermux) {
// log.error("Termux doesn't exits. Exit!");
// process.exit();
// }
var g_notification_id = 1;
//-----------------------------------------------------------------------------
/**
* A function that mocks the current location
* @returns {string}
*/
async function getCurrentLocation() {
let result = await api
.location()
.provider("gps") // network/passive/gps (default)
.request("last") // updates/last/once (default)
.run();
// log.info("current location result: ", JSON.stringify(result, null, 2));
return `${result.latitude},${result.longitude}`;
}
//-----------------------------------------------------------------------------
/**
* Our callback function once we get inside the fence
*/
async function insideGeofenceCallBack() {
api
.notification()
.content("We are inside the geofence!")
.id(g_notification_id + 1)
.title("Geofencing done")
// .url('...')
.run();
}
//-----------------------------------------------------------------------------
/**
* @returns {object}
*/
function buildNotification(updateDistanceResults) {
let notificationTitle = `
curDist: ${updateDistanceResults.curDistance.text},
curDur: ${updateDistanceResults.curDuration.text},
inside: ${updateDistanceResults.insideFence}`;
let notificationText = `Activates on:
${updateDistanceResults.activateFenceOn},
duration:${updateDistanceResults.fenceDurationValue},
distance:${updateDistanceResults.fenceDistanceValue}
`;
return {
title: notificationTitle,
text: notificationText
};
}
//-----------------------------------------------------------------------------
/**
* Callback function to be called whenever the current location and distance
* is updated
* @param {Object} updateDistanceResults
*/
async function updateDistanceCallBack(updateDistanceResults) {
let notification = buildNotification(updateDistanceResults);
api
.notification()
.content(notification.text)
.id(g_notification_id)
.title(notification.title)
// .url('...')
.run();
}
//-----------------------------------------------------------------------------
let options = {
apiKey: "ENTER YOUR API HERE",
updateInterval: 5,
getCurrentLocation: getCurrentLocation,
insideGeofenceCallBack: insideGeofenceCallBack,
updateDistanceCallBack: updateDistanceCallBack,
loopForever: true,
activateFenceOn: "duration", // 'duration', 'distance', 'either'
fenceDurationValue: 25 * 60, // range of the fence in seconds
fenceDistanceValue: 1000 // range of the fence in meter
};
let locationSepc = {
destination: "Oakland, CA",
mode: "driving"
};
//-----------------------------------------------------------------------------
async function init() {
let optionKeys = [
`apiKey`,
`activateFenceOn`,
`updateInterval`,
`loopForever`,
`fenceDurationValue`,
`fenceDistanceValue`
];
let locationSepcKeys = [`destination`, `mode`, `origin`];
var schema = {
properties: {
apiKey: {
description: "Enter your API key",
type: "string",
pattern: /[a-zA-Z0-9_]+$/,
message: "Value should only be alphanumberical",
// default: 10,
required: true
},
destination: {
description: "Enter destination (Address or lat,long)",
type: "string",
// pattern: /[a-zA-Z0-9_]+$/,
// message: "Value should only be alphanumberical",
default: "340 Main Street, Venice, CA, USA",
required: true
},
activateFenceOn: {
description: "Fence on 'duration' or 'distance' or 'either'?",
type: "string",
pattern: /\b(duration|distance|either)\b/,
message: "Value should only be duration|distance|either",
default: 'duration',
required: true
},
updateInterval: {
description: "Enter your update interval in seconds",
type: "number",
pattern: /^[0-9]+$/,
message: "Value should only be numerical",
default: 10,
required: true
},
fenceDurationValue: {
description: "Enter range of fence in seconds",
type: "number",
pattern: /^[0-9]+$/,
message: "Value should only be numerical",
default: 600,
required: true
},
fenceDistanceValue: {
description: "Enter range of fence in meters",
type: "number",
pattern: /^[0-9]+$/,
message: "Value should only be numerical",
default: 5000,
required: true
},
fenceDurationValue: {
description: "Enter range of fence in seconds",
type: "number",
pattern: /^[0-9]+$/,
message: "Value should only be numerical",
default: 600,
required: true
},
mode: {
description: "Mode (driving, biking, walking, ...)",
type: "string",
pattern: /[a-zA-Z]+$/,
message: "Value should only be characters",
default: "driving",
required: true
},
loopForever: {
description: "Keep the geofence active once getting inside the fence?",
type: "boolean",
pattern: /\b(true|false)\b/,
message: "Value should be Boolean",
default: true,
required: true
}
}
};
prompt.start();
let result = await promptGet(schema);
log.info("Command-line input received:");
optionKeys.forEach(e => {
if (result[e]) {
options[e] = result[e];
log.info(`${e}: ${options[e]}`);
}
});
locationSepcKeys.forEach(e => {
if (result[e]) {
locationSepc[e] = result[e];
log.info(`${e}: ${locationSepc[e]}`);
}
});
}
async function main(){
await init();
g_notification_id = utility_functions.hashCode(locationSepc.destination);
var geofence = require("../index.js")(options, locationSepc);
geofence.start(options);
}
main();