Skip to content

Commit

Permalink
new announcement feature
Browse files Browse the repository at this point in the history
allow notification to be created manually
support for routing these to special pushover and maker keys
support for multiple pushover and maker key
  • Loading branch information
jasoncalabrese committed Jul 25, 2015
1 parent c68a330 commit add721b
Show file tree
Hide file tree
Showing 15 changed files with 459 additions and 162 deletions.
15 changes: 9 additions & 6 deletions env.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ function config ( ) {

env.isEnabled = isEnabled;
env.anyEnabled = anyEnabled;
env.hasExtendedSetting = hasExtendedSetting;

return env;
}
Expand Down Expand Up @@ -136,13 +137,9 @@ function setEnableAndExtendedSettnigs() {
env.enable = 'ar2 ' + env.enable;
}

// For pushing notifications to Pushover.
//TODO: handle PUSHOVER_ as generic plugin props
env.pushover_api_token = readENV('PUSHOVER_API_TOKEN');
env.pushover_user_key = readENV('PUSHOVER_USER_KEY') || readENV('PUSHOVER_GROUP_KEY');
if (env.pushover_api_token && env.pushover_user_key) {
//don't require pushover to be enabled to preserve backwards compatibility if there are extendedSettings for it
if (hasExtendedSetting('PUSHOVER', process.env)) {
env.enable += ' pushover';
//TODO: after config changes are documented this shouldn't be auto enabled
}

if (anyEnabled(['careportal', 'pushover', 'maker'])) {
Expand Down Expand Up @@ -258,6 +255,12 @@ function readENV(varName, defaultValue) {
return value != null ? value : defaultValue;
}

function hasExtendedSetting(prefix, envs) {
return _.find(envs, function (value, key) {
return key.indexOf(prefix.toUpperCase() + '_') >= 0 || key.indexOf(prefix.toLowerCase() + '_') >= 0
}) !== undefined;
}

function findExtendedSettings (enables, envs) {
var extended = {};
enables.split(' ').forEach(function eachEnable(enable) {
Expand Down
4 changes: 4 additions & 0 deletions lib/levels.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ var level2Display = {
, '-3': 'None'
};

levels.isAlarm = function isAlarm(level) {
return level === levels.WARN || level === levels.URGENT;
};

levels.toDisplay = function toDisplay(level) {
var key = level !== undefined && level.toString();
return key && level2Display[key] || 'Unknown';
Expand Down
9 changes: 5 additions & 4 deletions lib/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,14 @@ function init (env, ctx) {
return _.find(requests.notifies, {level: levels.URGENT}) || _.find(requests.notifies, {level: levels.WARN});
};

notifications.findInfos = function findInfos ( ) {
notifications.findUnSnoozeable = function findUnSnoozeable ( ) {
return _.filter(requests.notifies, function (notify) {
return notify.level <= levels.INFO;
return notify.level <= levels.INFO || notify.announcement;
});
};

notifications.snoozedBy = function snoozedBy (notify) {
if (notify.announcement) { return false; }
if (_.isEmpty(requests.snoozes)) { return false; }

var byLevel = _.filter(requests.snoozes, function checkSnooze (snooze) {
Expand Down Expand Up @@ -128,8 +129,8 @@ function init (env, ctx) {
autoAckAlarms();
}

notifications.findInfos().forEach(function eachInfo (info) {
emitNotification(info);
notifications.findUnSnoozeable().forEach(function eachInfo (notify) {
emitNotification(notify);
});
};

Expand Down
61 changes: 41 additions & 20 deletions lib/plugins/maker.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
'use strict';

var _ = require('lodash');
var async = require('async');
var request = require('request');

function init (env) {

var key = env.extendedSettings && env.extendedSettings.maker && env.extendedSettings.maker.key;
var keys = env.extendedSettings && env.extendedSettings.maker &&
env.extendedSettings.maker.key && env.extendedSettings.maker.key.split(' ');

var announcementKeys = (env.extendedSettings && env.extendedSettings.maker &&
env.extendedSettings.maker.announcementKey && env.extendedSettings.maker.announcementKey.split(' ')) || keys;

var TIME_30_MINS_MS = 30 * 60 * 1000;

Expand All @@ -20,7 +25,7 @@ function init (env) {
//can be used to prevent maker/twitter deduping (add to IFTTT tweet text)
var shortTimestamp = Math.round(Date.now() / 1000 / 60);

maker.makeRequest({
maker.makeKeyRequests({
value1: (notify && notify.title) || 'All Clear'
, value2: notify && notify.message && '\n' + notify.message
, value3: '\n' + shortTimestamp
Expand All @@ -39,7 +44,11 @@ function init (env) {

maker.sendEvent = function sendEvent (event, callback) {
if (!event || !event.name) {
if (callback) { callback('No event name found'); }
if (callback) {
callback('No event name found');
}
} else if (!event.level) {
callback('No event level found');
} else {
maker.makeRequests(event, function sendCallback (err, response) {
if (err) {
Expand Down Expand Up @@ -73,37 +82,49 @@ function init (env) {
return query;
};

maker.makeRequest = function makeRequest(event, eventName, callback) {
var url = 'https://maker.ifttt.com/trigger/' + eventName + '/with/key/' + key + maker.valuesToQuery(event);
request
.get(url)
.on('response', function (response) {
console.info('sent maker request: ', url);
if (callback) { callback(null, response); }
})
.on('error', function (err) {
if (callback) { callback(err); }
});
};

maker.makeRequests = function makeRequests(event, callback) {
function sendGeneric (callback) {
maker.makeRequest(event, 'ns-event', callback);
maker.makeKeyRequests(event, 'ns-event', callback);
}

function sendByLevel (callback) {
maker.makeRequest (event, 'ns-' + event.level, callback);
maker.makeKeyRequests (event, 'ns-' + event.level, callback);
}

function sendByLevelAndName (callback) {
maker.makeRequest(event, 'ns' + ((event.level && '-' + event.level) || '') + '-' + event.name, callback);
maker.makeKeyRequests(event, 'ns' + ((event.level && '-' + event.level) || '') + '-' + event.name, callback);
}

//since maker events only filter on name, we are sending multiple events and different levels of granularity
async.series([sendGeneric, sendByLevel, sendByLevelAndName], callback);
};

if (key) {
maker.makeKeyRequests = function makeKeyRequests(event, eventName, callback) {
var selectedKeys = event.announcement ? announcementKeys : keys;

_.forEach(selectedKeys, function eachKey(key) {
maker.makeKeyRequest(key, event, eventName, callback);
});
};

maker.makeKeyRequest = function makeKeyRequest(key, event, eventName, callback) {
var url = 'https://maker.ifttt.com/trigger/' + eventName + '/with/key/' + key + maker.valuesToQuery(event);
request
.get(url)
.on('response', function (response) {
console.info('sent maker request: ', url);
if (callback) {
callback(null, response);
}
})
.on('error', function (err) {
if (callback) {
callback(err);
}
});
}

if (keys && keys.length > 0) {
return maker;
} else {
return null;
Expand Down
96 changes: 77 additions & 19 deletions lib/plugins/pushover.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,91 @@
'use strict';

var _ = require('lodash');
var Pushover = require('pushover-notifications');
var request = require('request');
var levels = require('../levels');

var TIME_2_MINS_S = 120
, TIME_15_MINS_S = 15 * 60
;

function init (env) {
var pushover = null;
var pushover = { };
var pushoverAPI = null;

var apiToken = env.extendedSettings && env.extendedSettings.pushover && env.extendedSettings.pushover.apiToken;

var userKeys = (env.extendedSettings && env.extendedSettings.pushover &&
env.extendedSettings.pushover.userKey && env.extendedSettings.pushover.userKey.split(' ')) || [];

if (env.pushover_api_token && env.pushover_user_key) {
pushover = new Pushover({
token: env.pushover_api_token,
user: env.pushover_user_key
var announcementKeys = (env.extendedSettings && env.extendedSettings.pushover &&
env.extendedSettings.pushover.announcementKey && env.extendedSettings.pushover.announcementKey.split(' ')) || userKeys;

if (apiToken && (userKeys.length > 0 || announcementKeys.length > 0)) {
pushoverAPI = new Pushover({
token: apiToken
});
}

pushover.PRIORITY_NORMAL = 0;
pushover.PRIORITY_EMERGENCY = 2;

pushover.cancelWithReceipt = function cancelWithReceipt (receipt, callback) {
request
.get('https://api.pushover.net/1/receipts/' + receipt + '/cancel.json?token=' + env.pushover_api_token)
.on('response', function(response) {
callback(null, response);
})
.on('error', function(err) {
callback(err);
});
pushover.PRIORITY_NORMAL = 0;
pushover.PRIORITY_EMERGENCY = 2;

pushover.send = function wrapSend (notify, callback) {

var selectedKeys = notify.announcement ? announcementKeys : userKeys;

var msg = {
expire: TIME_15_MINS_S
, title: notify.title
, message: notify.message
, sound: notify.pushoverSound || 'gamelan'
, timestamp: new Date()
//USE PUSHOVER_EMERGENCY for WARN and URGENT so we get the acks
, priority: notify.level >= levels.WARN ? pushover.PRIORITY_EMERGENCY : pushover.PRIORITY_NORMAL
};
}

return pushover;
if (levels.isAlarm(notify.level)) {
//ADJUST RETRY TIME based on WARN or URGENT
msg.retry = notify.level === levels.URGENT ? TIME_2_MINS_S : TIME_15_MINS_S;
if (env.baseUrl) {
msg.callback = env.baseUrl + '/api/v1/notifications/pushovercallback';
}
}

_.forEach(selectedKeys, function eachKey(key) {
msg.user = key;
pushover.sendAPIRequest(msg, callback);
});

};

pushover.sendAPIRequest = function sendAPIRequest (msg, callback) {
pushoverAPI.send(msg, function (err, result) {
if (err) {
console.error('unable to send pushover notification', err);
} else {
console.info('sent pushover notification: ', msg, 'result: ', result);
}
callback(err, result);
});
};

pushover.cancelWithReceipt = function cancelWithReceipt (receipt, callback) {
request
.get('https://api.pushover.net/1/receipts/' + receipt + '/cancel.json?token=' + apiToken)
.on('response', function(response) {
callback(null, response);
})
.on('error', function(err) {
callback(err);
});
};

if (pushoverAPI) {
return pushover;
} else {
return null;
}
}

module.exports = init;
73 changes: 35 additions & 38 deletions lib/plugins/simplealarms.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

var levels = require('../levels');

function init() {

var simplealarms = {
Expand All @@ -13,50 +15,18 @@ function init() {
simplealarms.checkNotifications = function checkNotifications(sbx) {
var lastSGVEntry = sbx.lastSGVEntry()
, scaledSGV = sbx.scaleEntry(lastSGVEntry)
, trigger = false
, level = 0
, title = ''
, pushoverSound = null
;

var eventName = '';

if (scaledSGV && lastSGVEntry && lastSGVEntry.mgdl > 39 && Date.now() - lastSGVEntry.mills < TIME_10_MINS_MS) {
if (scaledSGV > sbx.scaleMgdl(sbx.thresholds.bg_high)) {
trigger = true;
level = 2;
title = 'Urgent HIGH';
pushoverSound = 'persistent';
eventName = 'high';
} else if (scaledSGV > sbx.scaleMgdl(sbx.thresholds.bg_target_top)) {
trigger = true;
level = 1;
title = 'High warning';
pushoverSound = 'climb';
eventName = 'high';
} else if (scaledSGV < sbx.scaleMgdl(sbx.thresholds.bg_low)) {
trigger = true;
level = 2;
title = 'Urgent LOW';
pushoverSound = 'persistent';
eventName = 'low';
} else if (scaledSGV < sbx.scaleMgdl(sbx.thresholds.bg_target_bottom)) {
trigger = true;
level = 1;
title = 'Low warning';
pushoverSound = 'falling';
eventName = 'low';
console.info(title + ': ' + (scaledSGV + ' < ' + sbx.scaleMgdl(sbx.thresholds.bg_target_bottom)));
}

if (trigger) {
var result = simplealarms.compareBGToTresholds(scaledSGV, sbx);
if (levels.isAlarm(result.level)) {
sbx.notifications.requestNotify({
level: level
, title: title
level: result.level
, title: result.title
, message: sbx.buildDefaultMessage()
, eventName: eventName
, eventName: result.eventName
, plugin: simplealarms
, pushoverSound: pushoverSound
, pushoverSound: result.pushoverSound
, debug: {
lastSGV: scaledSGV, thresholds: sbx.thresholds
}
Expand All @@ -65,6 +35,33 @@ function init() {
}
};

simplealarms.compareBGToTresholds = function compareBGToTresholds(scaledSGV, sbx) {
var result = { level: levels.INFO };

if (scaledSGV > sbx.scaleMgdl(sbx.thresholds.bg_high)) {
result.level = levels.URGENT;
result.title = levels.toDisplay(levels.URGENT) + ' HIGH';
result.pushoverSound = 'persistent';
result.eventName = 'high';
} else if (scaledSGV > sbx.scaleMgdl(sbx.thresholds.bg_target_top)) {
result.level = levels.WARN;
result.title = levels.toDisplay(levels.WARN) + ' HIGH';
result.pushoverSound = 'climb';
result.eventName = 'high';
} else if (scaledSGV < sbx.scaleMgdl(sbx.thresholds.bg_low)) {
result.level = levels.URGENT;
result.title = levels.toDisplay(levels.URGENT) + ' LOW';
result.pushoverSound = 'persistent';
result.eventName = 'low';
} else if (scaledSGV < sbx.scaleMgdl(sbx.thresholds.bg_target_bottom)) {
result.level = levels.WARN;
result.title = levels.toDisplay(levels.WARN) + ' LOW';
result.pushoverSound = 'falling';
result.eventName = 'low';
}
return result;
};

return simplealarms;

}
Expand Down
Loading

0 comments on commit add721b

Please sign in to comment.