Skip to content

Instantly share code, notes, and snippets.

@joshbirk
Created September 25, 2017 20:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joshbirk/9c3fadb3956b9d55afc5a055bae3e697 to your computer and use it in GitHub Desktop.
Save joshbirk/9c3fadb3956b9d55afc5a055bae3e697 to your computer and use it in GitHub Desktop.
#require "Salesforce.class.nut:1.1.0"
#require "Rocky.class.nut:1.2.3"
#require "bullwinkle.class.nut:2.3.0"
#require "promise.class.nut:3.0.0"
// EXTEND SALESFORCE CLASS TO HANDLE OAUTH 2.0
// ----------------------------------------------------------
class SalesforceOAuth2 extends Salesforce {
_login = null;
constructor(consumerKey, consumerSecret, loginServiceBase = null, salesforceVersion = "v40.0") {
_clientId = consumerKey;
_clientSecret = consumerSecret;
if ("Rocky" in getroottable()) {
_login = Rocky();
} else {
throw "Unmet dependency: SalesforceOAuth2 requires Rocky";
}
if (loginServiceBase != null) _loginServiceBase = loginServiceBase;
if (salesforceVersion != null) _version = salesforceVersion;
getStoredCredentials();
defineLoginEndpoint();
}
function getStoredCredentials() {
local persist = server.load();
local oAuth = {};
if ("oAuth" in persist) oAuth = persist.oAuth;
// Load credentials if we have them
if ("instance_url" in oAuth && "access_token" in oAuth) {
// Set the credentials in the Salesforce object
setInstanceUrl(oAuth.instance_url);
setToken(oAuth.access_token);
// Log a message
server.log("Loaded OAuth Credentials!");
server.log(oAuth.instance_url);
}
}
function defineLoginEndpoint() {
// Define log in endpoint for a GET request to the agent URL
_login.get("/", function(context) {
// Check if an OAuth code was passed in
if (!("code" in context.req.query)) {
// If it wasn't, redirect to login service
local location = format("%s/services/oauth2/authorize?response_type=code&client_id=%s&redirect_uri=%s", _loginServiceBase, _clientId, http.agenturl());
context.setHeader("Location", location);
context.send(302, "Found");
return;
}
// Exchange the auth code for inan OAuth token
getOAuthToken(context.req.query["code"], function(err, resp, respData) {
if (err) {
context.send(400, "Error authenticating (" + err + ").");
return;
}
// If it was successful, save the data locally
local persist = { "oAuth" : respData };
server.save(persist);
// Set/update the credentials in the Salesforce object
setInstanceUrl(persist.oAuth.instance_url);
setToken(persist.oAuth.access_token);
// Finally - inform the user we're done!
context.send(200, "Authentication complete - you may now close this window");
}.bindenv(this));
}.bindenv(this));
}
// OAuth 2.0 methods
function getOAuthToken(code, cb) {
// Send request with an authorization code
_oauthTokenRequest("authorization_code", code, cb);
}
function refreshOAuthToken(refreshToken, cb) {
// Send request with refresh token
_oauthTokenRequest("refresh_token", refreshToken, cb);
}
function _oauthTokenRequest(type, tokenCode, cb = null) {
// Build the request
local url = format("%s/services/oauth2/token", _loginServiceBase);
local headers = { "Content-Type": "application/x-www-form-urlencoded" };
local data = {
"grant_type": type,
"client_id": _clientId,
"client_secret": _clientSecret,
};
// Set the "code" or "refresh_token" parameters based on grant_type
if (type == "authorization_code") {
data.code <- tokenCode;
data.redirect_uri <- http.agenturl();
} else if (type == "refresh_token") {
data.refresh_token <- tokenCode;
} else {
throw "Unknown grant_type";
}
local body = http.urlencode(data);
http.post(url, headers, body).sendasync(function(resp) {
local respData = http.jsondecode(resp.body);
local err = null;
// If there was an error, set the error code
if (resp.statuscode != 200) err = data.message;
// Invoke the callback
if (cb) imp.wakeup(0, function() { cb(err, resp, respData); });
});
}
}
/***************************************************************************************
* SmartFrigDataManager Class:
* Handle incoming device readings
* Set sensor threshold values
* Set callback handlers for events and streaming data
* Check for temperature, humidity, and door events
* Average temperature and humidity readings
**************************************************************************************/
class SmartFrigDataManager {
// Default settings
static DEFAULT_LX_THRESHOLD = 50; // LX level indicating door open
static DEFAULT_TEMP_THRESHOLD = 11;
static DEFAULT_HUMID_THRESHOLD = 70;
// NOTE: changing the device reading or reporting intervals will impact timing of event and alert conditions
static DOOR_OPEN_ALERT = 10; // Number of reading cycles before activating a door alert (currently 30s: DOOR_OPEN_ALERT * device reading interval = seconds before sending door alert)
static CLEAR_DOOR_OPEN_EVENT = 180; // Clear door open event after num seconds (prevents temperature or humidity alerts right after is opened)
static TEMP_ALERT_CONDITION = 300; // Number of seconds the temperature must be over threshold before triggering event
static HUMID_ALERT_CONDITION = 300; // Number of seconds the humidity must be over threshold before triggering event
// Class variables
_bull = null;
// Threshold
_tempThreshold = null;
_humidThreshold = null;
_lxThreshold = null;
_thresholdsUpdated = null;
// Alert flags and counters
_doorOpenTS = null;
_doorOpenCounter = null;
_doorOpenAlertTriggered = null;
_tempAlertTriggered = null;
_humidAlertTriggered = null;
_tempEventTime = null;
_humidEventTime = null;
// Event handlers
_doorOpenHandler = null;
_streamReadingsHandler = null;
_tempAlertHandler = null;
_humidAlertHandler = null;
/***************************************************************************************
* Constructor
* Returns: null
* Parameters:
* bullwinkle : instance - of Bullwinkle class
**************************************************************************************/
constructor(bullwinkle) {
_bull = bullwinkle;
setThresholds(DEFAULT_TEMP_THRESHOLD, DEFAULT_HUMID_THRESHOLD, DEFAULT_LX_THRESHOLD);
_doorOpenCounter = 0;
openListeners();
}
/***************************************************************************************
* openListeners
* Returns: this
* Parameters: none
**************************************************************************************/
function openListeners() {
_bull.on("readings", _readingsHandler.bindenv(this));
_bull.on("lxThreshold", _lxThresholdHandler.bindenv(this));
return this;
}
/***************************************************************************************
* setThresholds
* Returns: null
* Parameters:
* temp : integer - new tempertature threshold value
* humid : integer - new humid threshold value
* lx : integer - new light level door value
**************************************************************************************/
function setThresholds(temp, humid, lx) {
_tempThreshold = temp;
_humidThreshold = humid;
_lxThreshold = lx;
_thresholdsUpdated = true;
}
/***************************************************************************************
* setDoorOpenHandler
* Returns: null
* Parameters:
* cb : function - called when door open alert triggered
**************************************************************************************/
function setDoorOpenHandler(cb) {
_doorOpenHandler = cb;
}
/***************************************************************************************
* setStreamReadingsHandler
* Returns: null
* Parameters:
* cb : function - called when new reading received
**************************************************************************************/
function setStreamReadingsHandler(cb) {
_streamReadingsHandler = cb;
}
/***************************************************************************************
* setTempAlertHandler
* Returns: null
* Parameters:
* cb : function - called when temperature alert triggerd
**************************************************************************************/
function setTempAlertHandler(cb) {
_tempAlertHandler = cb;
}
/***************************************************************************************
* setHumidAlertHandler
* Returns: null
* Parameters:
* cb : function - called when humidity alert triggerd
**************************************************************************************/
function setHumidAlertHandler(cb) {
_humidAlertHandler = cb;
}
/***************************************************************************************
* _lxThresholdHandler
* Returns: null
* Parameters:
* message : table - message received from bullwinkle listener
* reply: function that sends a reply to bullwinle message sender
**************************************************************************************/
function _lxThresholdHandler(message, reply) {
if (_thresholdsUpdated) {
reply(_lxThreshold);
_thresholdsUpdated = false;
} else {
reply(null);
}
}
/***************************************************************************************
* _readingsHandler
* Returns: null
* Parameters:
* message : table - message received from bullwinkle listener
* reply: function that sends a reply to bullwinle message sender
**************************************************************************************/
function _readingsHandler(message, reply) {
// grab readings array from message
local readings = message.data;
// set up variables for calculating reading average
local tempAvg = 0;
local humidAvg = 0
local numReadings = 0;
// set up variables for door event
local doorOpen = null;
local ts = null;
// process readings
// reading table keys : "brightness", "humidity", "temperature", "ts"
foreach(reading in readings) {
// calculate temperature and humidity totals
if ("temperature" in reading && "humidity" in reading) {
numReadings++;
tempAvg += reading.temperature;
humidAvg += reading.humidity;
}
// get time stamp of reading
ts = reading.ts;
// determine door status
if ("brightness" in reading) doorOpen = _checkDoorEvent(ts, reading.brightness);
}
if (numReadings != 0) {
// average the temperature and humidity readings
tempAvg = tempAvg/numReadings;
humidAvg = humidAvg/numReadings;
// check for events
_checkTempEvent(tempAvg, ts);
_checkHumidEvent(humidAvg, ts);
}
// send reading to handler
_streamReadingsHandler({"temperature" : tempAvg, "humidity" : humidAvg, "door" : doorOpen}, ts);
// send ack to device (device erases this set of readings when ack received)
reply("OK");
}
/***************************************************************************************
* _checkTempEvent
* Returns: null
* Parameters:
* reading : float - a temperature reading
**************************************************************************************/
function _checkTempEvent(reading, ts) {
// check for temp event
if (reading > _tempThreshold) {
// check that frig door hasn't been open recently & that alert hasn't been sent
if (_doorOpenTS == null && !_tempAlertTriggered) {
// create event timer
if (_tempEventTime == null) {
_tempEventTime = ts + TEMP_ALERT_CONDITION;
}
// check that alert conditions have exceeded the time needed to trigger alert
if (ts >= _tempEventTime) {
// Trigger Temp Alert
_tempAlertHandler(reading, ts, _tempThreshold);
// Set flag so we don't trigger the same alert again
_tempAlertTriggered = true;
// Reset Temp Event timer
_tempEventTime = null;
}
}
} else {
// Reset Temp Alert Conditions
_tempAlertTriggered = false;
_tempEventTime = null;
}
}
/***************************************************************************************
* _checkHumidEvent
* Returns: null
* Parameters:
* reading : float - a humidity reading
**************************************************************************************/
function _checkHumidEvent(reading, ts) {
// check for humidity event
if (reading > _humidThreshold) {
// check that frig door hasn't been open recently & that alert hasn't been sent
if (_doorOpenTS == null && !_humidAlertTriggered) {
// create event timer
if (_humidEventTime == null) {
_humidEventTime = ts + HUMID_ALERT_CONDITION;
}
// check that alert conditions have exceeded the time needed to trigger alert
if ( ts >= _humidEventTime) {
// Trigger Humidity Alert
_humidAlertHandler(reading, ts, _humidThreshold);
// Set flag so we don't trigger the same alert again
_humidAlertTriggered = true;
// Reset Humidity timer
_humidEventTime = null;
}
}
} else {
// Reset Hmidity Alert Conditions
_humidAlertTriggered = false;
_humidEventTime = null;
}
}
/***************************************************************************************
* _checkDoorEvent
* Returns: sting - door status
* Parameters:
* lxLevel : float - a light reading
* readingTS : integer - the timestamp of the reading
**************************************************************************************/
function _checkDoorEvent(readingTS, lxLevel = null) {
// Boolean if door open event occurred
local doorOpen = (lxLevel == null || lxLevel > _lxThreshold);
//fake it
//doorOpen = true;
// check if door open
if (doorOpen) {
_doorOpenCounter++;
// check if door timer started
if (!_doorOpenTS) {
// start door timer
_doorOpenTS = readingTS;
// check that door alert conditions have been met
} else if (!_doorOpenAlertTriggered && _doorOpenCounter > DOOR_OPEN_ALERT) {
// trigger door open alert
_doorOpenAlertTriggered = readingTS;
_doorOpenHandler(readingTS - _doorOpenTS);
}
} else {
// since door is closed, reset door open alert conditions
_doorOpenCounter = 0;
_doorOpenAlertTriggered = null;
// check that door timer can be reset
if (_doorOpenTS && (readingTS - _doorOpenTS) >= CLEAR_DOOR_OPEN_EVENT ) {
// since door closed for set ammount of time, reset door event timer
_doorOpenTS = null;
}
}
return (doorOpen) ? "Open" : "Closed";
}
}
// APPLICATION CLASS TO SEND FRIG DATA/ALERTS TO SALESFORCE
// ----------------------------------------------------------
class Application {
_dm = null;
_force = null;
_deviceID = null;
_objName = null;
constructor(key, secret, objName) {
_deviceID = imp.configparams.deviceid.tostring();
_objName = objName;
initializeClasses(key, secret);
setDataMngrHandlers();
}
function initializeClasses(key, secret) {
local _bull = Bullwinkle();
_dm = SmartFrigDataManager(_bull);
_force = SalesforceOAuth2(key, secret);
}
function setDataMngrHandlers() {
_dm.setDoorOpenHandler(doorOpenHandler.bindenv(this));
_dm.setStreamReadingsHandler(streamReadingsHandler.bindenv(this));
_dm.setTempAlertHandler(tempAlertHandler.bindenv(this));
_dm.setHumidAlertHandler(humidAlertHandler.bindenv(this));
}
function updateRecord(data, cb = null) {
//local url = format("sobjects/%s/DeviceId__c/%s?_HttpMethod=PATCH", _objName, _deviceID);
local url = format("sobjects/%s",_objName);
local body = {};
// add salesforce custom object postfix to data keys
foreach(k, v in data) {
body[k + "__c"] <- v;
}
// don't send if we are not logged in
if (!_force.isLoggedIn()) {
server.error("Not logged into saleforce.")
return;
}
_force.request("POST", url, http.jsonencode(body), cb);
}
function openCase(subject, description, cb = null) {
local data = {
"Subject": subject,
"Description": description,
"Related_Fridge__r" : {"DeviceId__c": _deviceID}
};
// don't send if we are not logged in
if (!_force.isLoggedIn()) {
server.error("Not logged into saleforce.")
return;
}
_force.request("POST", "sobjects/Case", http.jsonencode(data), cb);
}
function streamReadingsHandler(reading, ts) {
reading.ts <- ts;
server.log(http.jsonencode(reading));
updateRecord(reading, updateRecordResHandler);
}
function doorOpenHandler(doorOpenFor) {
local alert = "Refrigerator Door Open";
local description = format("Refrigerator with id %s door has been open for %s seconds.", _deviceID, doorOpenFor.tostring());
server.log("Door Open Alert: door has been open for " + doorOpenFor + " seconds.");
// openCase(alert, description, caseResponseHandler);
}
function tempAlertHandler(latestReading, alertTiggeredTime, threshold) {
local alert = "Temperature Over Threshold";
local description = format("Refrigerator with id %s temperature above %s °C. Refrigerator temperature is %s °C", _deviceID, threshold.tostring(), latestReading.tostring());
server.log(alert + ": " + description);
// openCase(alert, description, caseResponseHandler);
}
function humidAlertHandler(latestReading, alertTiggeredTime, threshold) {
local alert = "Humidity Over Threshold";
local description = format("Refrigerator with id %s humidity above %s%s. Refrigerator humidity is %s%s", _deviceID, threshold.tostring(), "%", latestReading.tostring(), "%");
server.log(alert + ": " + description);
// openCase(alert, description, caseResponseHandler);
}
function caseResponseHandler(err, data) {
if (err) {
server.error(http.jsonencode(err));
return;
}
server.log("Created case with id: " + data.id);
}
function updateRecordResHandler(err, respData) {
if (err) {
server.error(http.jsonencode(err));
return;
}
// Log a message for creating/updating a record
if ("success" in respData) {
server.log("Record created: " + respData.success);
}
}
}
// RUNTIME
// ---------------------------------------------------------------------------------
// SALESFORCE CONSTANTS
// ----------------------------------------------------------
//const CONSUMER_KEY = "3MVG9sG9Z3Q1RlbdgwDkzM3OQ0pucpBYYFHlKAiePxdYMpukQS6h5HZLXOr1UyJ8LN6eX2CHqpptOvPbro9C8";
//const CONSUMER_SECRET = "4373675996771098708";
//const CONSUMER_KEY = "3MVG9sG9Z3Q1RlbdgwDkzM3OQ0pCVPoENO6ckeLqNiiNEwnfivfT4sNlf34iLg_6zN28uYnYhSZ6xLesIMJIc";
//const CONSUMER_SECRET = "3331888167643794103";
const CONSUMER_KEY = "3MVG9SemV5D80oBcJQ6GPMXa9wIZr_GGASUCL67QrZOhmrq3hEgaQDlBNI27DrjF5RGja1.ltTEbiOAihrGh_";
const CONSUMER_SECRET = "9009196932862200945";
const OBJ_API_NAME = "Imp__e"
Application(CONSUMER_KEY, CONSUMER_SECRET, OBJ_API_NAME);
// Temperature Humidity sensor Library
#require "Si702x.class.nut:1.0.0"
// Air Pressure sensor Library
#require "LPS25H.class.nut:2.0.1"
// Ambient Light sensor Library
#require "APDS9007.class.nut:2.2.1"
#require "ConnectionManager.class.nut:1.0.1"
#require "promise.class.nut:3.0.0"
#require "bullwinkle.class.nut:2.3.0"
/***************************************************************************************
* EnvTail Class:
* Initializes and enables sensors specified in constructor
* Set time interval between readings
* Get time interval between readings
* Takes sensor readings & stores them to local device storage
**************************************************************************************/
class EnvTail {
_tempHumid = null;
_ambLx = null;
_press = null;
_led = null;
_cm = null;
_readingInterval = null;
/***************************************************************************************
* Constructor
* Returns: null
* Parameters:
* enableTempHumid : boolean - if the temperature/humidity sensor should be enabled
* enableAmbLx : boolean - if the ambient light sensor should be enabled
* enablePressure : boolean - if the air pressure sensor should be enabled
* readingInt : second to wait between readings
**************************************************************************************/
constructor(enableTempHumid, enableAmbLx, enablePressure, readingInt, cm = null) {
_cm = cm;
_configureLED();
_configureNVTable();
_enableSensors(enableTempHumid, enableAmbLx, enablePressure);
setReadingInterval(readingInt);
}
/***************************************************************************************
* takeReadings - takes readings, sends to agent, schedules next reading
* Returns: null
* Parameters:
* cb (optional) : function - callback that is passed the parsed reading
**************************************************************************************/
function takeReadings(cb = null) {
// Take readings asynchonously if sensor enabled
local que = _buildReadingQue();
// When all sensors have returned values store a reading locally
// Then set timer for next reading
Promise.all(que)
.then(function(envReadings) {
local reading = _parseReadings(envReadings);
// store reading
nv.readings.push(reading);
// pass reading to callback
if (cb) imp.wakeup(0, function() {
cb(reading);
}.bindenv(this));
// flash led to let user know a reading was stored
flashLed();
}.bindenv(this))
.finally(function(val) {
// set timer for next reading
imp.wakeup(_readingInterval, function() {
takeReadings(cb);
}.bindenv(this));
}.bindenv(this));
}
/***************************************************************************************
* setReadingInterval
* Returns: this
* Parameters:
* interval (optional) : the time in seconds to wait between readings,
* if nothing passed in sets the readingInterval to
* the default of 300s
**************************************************************************************/
function setReadingInterval(interval) {
_readingInterval = interval;
return this;
}
/***************************************************************************************
* getReadingInterval
* Returns: the current reading interval
* Parameters: none
**************************************************************************************/
function getReadingInterval() {
return _readingInterval;
}
/***************************************************************************************
* flashLed - blinks the led, this function blocks for 0.5s
* Returns: this
* Parameters: none
**************************************************************************************/
function flashLed() {
led.write(1);
imp.sleep(0.5);
led.write(0);
return this;
}
// ------------------------- PRIVATE FUNCTIONS ------------------------------------------
/***************************************************************************************
* _configureNVTable
* Returns: null
* Parameters: none
**************************************************************************************/
function _configureNVTable() {
local root = getroottable();
if (!("nv" in root)) root.nv <- { "readings" : [] };
}
/***************************************************************************************
* _buildReadingQue
* Returns: an array of Promises for each sensor that is taking a reading
* Parameters: none
**************************************************************************************/
function _buildReadingQue() {
local que = [];
if (_ambLx) que.push( _takeReading(_ambLx) );
if (_tempHumid) que.push( _takeReading(_tempHumid) );
if (_press) que.push( _takeReading(_press) );
return que;
}
/***************************************************************************************
* _takeReading
* Returns: Promise that resolves with the sensor reading
* Parameters:
* sensor: instance - the sensor to take a reading from
**************************************************************************************/
function _takeReading(sensor) {
return Promise(function(resolve, reject) {
sensor.read(function(reading) {
return resolve(reading);
}.bindenv(sensor));
}.bindenv(this))
}
/***************************************************************************************
* _parseReadings
* Returns: a table of successful readings
* Parameters:
* readings: array - with each sensor reading/error
**************************************************************************************/
function _parseReadings(readings) {
// add time stamp to reading
local data = {"ts" : time()};
// log error or store value of reading
foreach(reading in readings) {
if ("err" in reading) {
(_cm) ? _cm.error(reading.err) : server.error(reading.err);
} else if ("error" in reading) {
(_cm) ? _cm.error(reading.error) : server.error(reading.error);
} else {
foreach(sensor, value in reading) {
data[sensor] <- value;
}
}
}
return data;
}
/***************************************************************************************
* _enableSensors
* Returns: this
* Parameters:
* tempHumid: boolean - if temperature/humidity sensor should be enabled
* ambLx: boolean - if ambient light sensor should be enabled
* press: boolean - if air pressure sensor should be enabled
**************************************************************************************/
function _enableSensors(tempHumid, ambLx, press) {
if (tempHumid || press) _configure_i2cSensors(tempHumid, press);
if (ambLx) _configureAmbLx();
return this;
}
/***************************************************************************************
* _configure_i2cSensors
* Returns: this
* Parameters:
* tempHumid: boolean - if temperature/humidity sensor should be enabled
* press: boolean - if air pressure sensor should be enabled
**************************************************************************************/
function _configure_i2cSensors(tempHumid, press) {
local i2c = hardware.i2c89;
i2c.configure(CLOCK_SPEED_400_KHZ);
if (tempHumid) _tempHumid = Si702x(i2c);
if (press) {
_press = LPS25H(i2c);
// set up to take readings
_press.softReset();
_press.enable(true);
}
return this;
}
/***************************************************************************************
* _configureAmbLx
* Returns: this
* Parameters: none
**************************************************************************************/
function _configureAmbLx() {
local lxOutPin = hardware.pin5;
local lxEnPin = hardware.pin7;
lxOutPin.configure(ANALOG_IN);
lxEnPin.configure(DIGITAL_OUT, 1);
_ambLx = APDS9007(lxOutPin, 47000, lxEnPin);
_ambLx.enable();
return this;
}
/***************************************************************************************
* _configureLED
* Returns: this
* Parameters: none
**************************************************************************************/
function _configureLED() {
_led = hardware.pin2;
_led.configure(DIGITAL_OUT, 0);
return this;
}
}
/***************************************************************************************
* Application Class:
* Starts off reading loop
* Sends readings to agent
**************************************************************************************/
class Application {
static DEFAULT_READING_INTERVAL = 3;
static DEFAULT_REPORTING_INTERVAL = 15;
static DEFAULT_LX_THRESHOLD = 50; // LX level indicating door open
_bull = null;
_cm = null;
_tail = null;
_readinInt = null;
_reportingInt = null;
_lxThreshold = null;
_doorOpen = null;
_reportingTimer = null;
/***************************************************************************************
* constructor
* Returns: null
* Parameters:
* readingInt : integer - time interval in seconds between readings
* reportingInt : integer - time interval in seconds between connections to agent
**************************************************************************************/
constructor(readingInt = null, reportingInt = null) {
// configure class variables
_readinInt = (readingInt == null) ? DEFAULT_READING_INTERVAL : readingInt;
_reportingInt = (reportingInt == null) ? DEFAULT_REPORTING_INTERVAL : reportingInt;
_initializeClasses();
imp.wakeup(0.2, _getLXThreshold.bindenv(this));
}
/***************************************************************************************
* run - starts reading loop, starts reporting loop
* Returns: null
* Parameters: none
**************************************************************************************/
function run() {
// set doorOpen flag
_doorOpen = false;
// start reading loop
_tail.takeReadings(checkForDoorEvent.bindenv(this));
// send readings everytime we connect to server
_cm.onConnect(sendReadings.bindenv(this));
// wait one cycle then connect and send readings
_reportingTimer = imp.wakeup(_reportingInt, _cm.connect.bindenv(_cm));
}
/***************************************************************************************
* checkForDoorEvent
* Returns: null
* Parameters:
* reading : table of sensor readings
**************************************************************************************/
function checkForDoorEvent(reading) {
// set default if no lighting threshold has been received from agent
if (_lxThreshold == null) _lxThreshold = DEFAULT_LX_THRESHOLD;
if ("brightness" in reading && reading.brightness > _lxThreshold) {
// cancel the reporting timer
imp.cancelwakeup(_reportingTimer);
_doorOpen = true;
// wake up now and send change in door status
_cm.connect();
} else if (_doorOpen) {
// door was just closed
// cancel the reporting timer
imp.cancelwakeup(_reportingTimer);
_doorOpen = false;
// wake up now and send change in door status
_cm.connect();
}
}
/***************************************************************************************
* sendReadings - send readings from local storage to agent
* then clear local storage & disconnect
* Returns: null
* Parameters: none
**************************************************************************************/
function sendReadings() {
// check for readings
if ("nv" in getroottable() && "readings" in nv) {
// send readings to agent
_bull.send("readings", nv.readings)
// if agent receives readings
// erase them from local storage then disonncet
.onReply(function(msg) {
nv.readings = [];
_cm.disconnect();
}.bindenv(this))
// if connection fails just disconnect
// readings will be kept and sent on next connection
.onFail(function(err, msg, retry) {
_cm.disconnect();
}.bindenv(this));
} else {
// if no readings are available disconnect
_cm.disconnect();
}
// schedule next connection
_reportingTimer = imp.wakeup(_reportingInt, _cm.connect.bindenv(_cm));
}
// ------------------------- PRIVATE FUNCTIONS ------------------------------------------
/***************************************************************************************
* _initializeClasses
* Returns: null
* Parameters: none
**************************************************************************************/
function _initializeClasses() {
// agent/device communication helper library
_bull = Bullwinkle();
// connection helper library
_cm = ConnectionManager({"blinkupBehavior" : ConnectionManager.BLINK_ALWAYS});
// Class to manage sensors
_tail = EnvTail(true, true, false, _readinInt, _cm);
}
/***************************************************************************************
* _getLXThreshold
* Returns: null
* Parameters: none
**************************************************************************************/
function _getLXThreshold() {
_bull.send("lxThreshold", null)
.onReply(function(message) {
if (message.data != null) {
_lxThreshold = message.data;
}
}.bindenv(this));
}
}
// RUNTIME
// ----------------------------------------------
// Create instances of our classes
app <- Application();
// Give agent time to configure watson device
// Then start the sensor reading & connection loops
imp.wakeup(5, app.run.bindenv(app));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment