Skip to content

Commit

Permalink
support for disabling default features
Browse files Browse the repository at this point in the history
use decodeURIComponent to support environments where spaces in env vars are a problem
converted enable and alarmTypes to arrays
  • Loading branch information
jasoncalabrese committed Aug 23, 2015
1 parent 6828a73 commit 1779e61
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 183 deletions.
31 changes: 21 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Community maintained fork of the
- [Core](#core)
- [Predefined values for your browser settings (optional)](#predefined-values-for-your-browser-settings-optional)
- [Plugins](#plugins)
- [Default Plugins](#default-plugins)
- [Built-in/Example Plugins:](#built-inexample-plugins)
- [Extended Settings](#extended-settings)
- [Pushover](#pushover)
- [IFTTT Maker](#ifttt-maker)
Expand Down Expand Up @@ -119,7 +121,8 @@ Use the [autoconfigure tool][autoconfigure] to sync an uploader to your config.

### Features/Labs

* `ENABLE` - Used to enable optional features, expects a space delimited list such as: `careportal rawbg iob`, see [plugins](#plugins) below
* `ENABLE` - Used to enable optional features, expects a space delimited list, such as: `careportal rawbg iob`, see [plugins](#plugins) below
* `DISABLE` - Used to disable default features, expects a space delimited list, such as: `direction upbat`, see [plugins](#plugins) below
* `API_SECRET` - A secret passphrase that must be at least 12 characters long, required to enable `POST` and `PUT`; also required for the Care Portal
* `BG_HIGH` (`260`) - must be set using mg/dl units; the high BG outside the target range that is considered urgent
* `BG_TARGET_TOP` (`180`) - must be set using mg/dl units; the top of the target range, also used to draw the line on the chart
Expand Down Expand Up @@ -164,7 +167,23 @@ Use the [autoconfigure tool][autoconfigure] to sync an uploader to your config.

The built-in/example plugins that are available by default are listed below. The plugins may still need to be enabled by adding to the `ENABLE` environment variable.

**Built-in/Example Plugins:**
#### Default Plugins

These can be disabled by setting the `DISABLE` env var, for example `DISABLE="direction upbat"`

* `delta` (BG Delta) - Calculates and displays the change between the last 2 BG values.
* `direction` (BG Direction) - Displays the trend direction.
* `upbat` (Uploader Battery) - Displays the most recent battery status from the uploader phone.
* `errorcodes` (CGM Error Codes) - Generates alarms for CGM codes `9` (hourglass) and `10` (???).
* `ar2` ([Forcasting using AR2 algorithm](https://github.com/nightscout/nightscout.github.io/wiki/Forecasting)) - Generates alarms based on forecasted values.
* Enabled by default if no thresholds are set **OR** `ALARM_TYPES` includes `predict`.
* Use [extended settings](#extended-settings) to adjust AR2 behavior:
* `AR2_USE_RAW` (`false`) - to forecast using `rawbg` values when standard values don't trigger an alarm.
* `AR2_CONE_FACTOR` (`2`) - to adjust size of cone, use `0` for a single line.
* `simplealarms` (Simple BG Alarms) - Uses `BG_HIGH`, `BG_TARGET_TOP`, `BG_TARGET_BOTTOM`, `BG_LOW` thresholds to generate alarms.
* Enabled by default if 1 of these thresholds is set **OR** `ALARM_TYPES` includes `simple`.

#### Built-in/Example Plugins:

* `rawbg` (Raw BG) - Calculates BG using sensor and calibration records from and displays an alternate BG values and noise levels.
* `iob` (Insulin-on-Board) - Adds the IOB pill visualization in the client and calculates values that used by other plugins. Uses treatments with insulin doses and the `dia` and `sens` fields from the [treatment profile](#treatment-profile).
Expand All @@ -179,14 +198,6 @@ Use the [autoconfigure tool][autoconfigure] to sync an uploader to your config.
* `CAGE_INFO` (`44`) - If time since last `Site Change` matches `CAGE_INFO`, user will be warned of upcoming cannula change
* `CAGE_WARN` (`48`) - If time since last `Site Change` matches `CAGE_WARN`, user will be alarmed to to change the cannula
* `CAGE_URGENT` (`72`) - If time since last `Site Change` matches `CAGE_URGENT`, user will be issued a persistent warning of overdue change.
* `delta` (BG Delta) - Calculates and displays the change between the last 2 BG values. **Enabled by default.**
* `direction` (BG Direction) - Displays the trend direction. **Enabled by default.**
* `upbat` (Uploader Battery) - Displays the most recent battery status from the uploader phone. **Enabled by default.**
* `ar2` ([Forcasting using AR2 algorithm](https://github.com/nightscout/nightscout.github.io/wiki/Forecasting)) - Generates alarms based on forecasted values. **Enabled by default.** Use [extended settings](#extended-settings) to adjust AR2 behavior:
* `AR2_USE_RAW` (`false`) - to forecast using `rawbg` values when standard values don't trigger an alarm.
* `AR2_CONE_FACTOR` (`2`) - to adjust size of cone, use `0` for a single line.
* `simplealarms` (Simple BG Alarms) - Uses `BG_HIGH`, `BG_TARGET_TOP`, `BG_TARGET_BOTTOM`, `BG_LOW` settings to generate alarms.
* `errorcodes` (CGM Error Codes) - Generates alarms for CGM codes `9` (hourglass) and `10` (???). **Enabled by default.**
* `treatmentnotify` (Treatment Notifications) - Generates notifications when a treatment has been entered and snoozes alarms minutes after a treatment. Default snooze is 10 minutes, and can be set using the `TREATMENTNOTIFY_SNOOZE_MINS` [extended setting](#extended-settings).
* `basal` (Basal Profile) - Adds the Basal pill visualization to display the basal rate for the current time. Also enables the `bwp` plugin to calculate correction temp basal suggestions. Uses the `basal` field from the [treatment profile](#treatment-profile).
* `bridge` (Share2Nightscout bridge) - Glucose reading directly from the Share service, uses these extended settings:
Expand Down
48 changes: 3 additions & 45 deletions env.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ function config ( ) {
setMongo();
updateSettings();

env.hasExtendedSetting = hasExtendedSetting;

return env;
}

Expand Down Expand Up @@ -113,26 +111,6 @@ function setMongo() {

function updateSettings() {

var enable = readENV('ENABLE', '');

function anyEnabled (features) {
return _.findIndex(features, function (feature) {
return enable.indexOf(feature) > -1;
}) > -1;
}

//don't require pushover to be enabled to preserve backwards compatibility if there are extendedSettings for it
if (hasExtendedSetting('PUSHOVER', process.env)) {
enable += ' pushover';
}

if (anyEnabled(['careportal', 'pushover', 'maker'])) {
enable += ' treatmentnotify';
}

//TODO: figure out something for default plugins, how can they be disabled?
enable += ' delta direction upbat errorcodes';

var envNameOverrides = {
UNITS: 'DISPLAY_UNITS'
};
Expand All @@ -142,19 +120,8 @@ function updateSettings() {
return readENV(envName);
});

//TODO: maybe get rid of ALARM_TYPES and only use enable?
if (env.settings.alarmTypes.indexOf('simple') > -1) {
enable = 'simplealarms ' + enable;
}
if (env.settings.alarmTypes.indexOf('predict') > -1) {
enable = 'ar2 ' + enable;
}

env.settings.enable = enable;
env.settings.processRawSettings();

//should always find extended settings last
env.extendedSettings = findExtendedSettings(enable, process.env);
env.extendedSettings = findExtendedSettings(process.env);
}

function readENV(varName, defaultValue) {
Expand All @@ -170,23 +137,14 @@ function readENV(varName, defaultValue) {
return value != null ? value : defaultValue;
}

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

function findExtendedSettings (enables, envs) {
function findExtendedSettings (envs) {
var extended = {};

function normalizeEnv (key) {
return key.toUpperCase().replace('CUSTOMCONNSTR_', '');
}

enables.split(' ').forEach(function eachEnable(enable) {
_.forEach(env.settings.enable, function eachEnable(enable) {
if (_.trim(enable)) {
_.forIn(envs, function eachEnvPair (value, key) {
var env = normalizeEnv(key);
Expand Down
10 changes: 5 additions & 5 deletions lib/api/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use strict';

function create (env, ctx) {
var express = require('express'),
app = express( )
;
var _ = require('lodash')
, express = require('express')
, app = express( )
;

var wares = require('../middleware/')(env);

Expand All @@ -25,8 +26,7 @@ function create (env, ctx) {

if (env.settings.enable) {
app.extendedClientSettings = ctx.plugins && ctx.plugins.extendedClientSettings ? ctx.plugins.extendedClientSettings(env.extendedSettings) : {};
env.settings.enable.toLowerCase().split(' ').forEach(function (value) {
var enable = value.trim();
_.each(env.settings.enable, function (enable) {
console.info('enabling feature:', enable);
app.enable(enable);
});
Expand Down
124 changes: 87 additions & 37 deletions lib/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ function init ( ) {
, customTitle: 'Nightscout'
, theme: 'default'
, alarmUrgentHigh: true
, alarmTypes: 'predict'
, alarmHigh: true
, alarmLow: true
, alarmUrgentLow: true
Expand All @@ -30,54 +29,108 @@ function init ( ) {
}
};

settings.DEFAULT_FEATURES = ['delta', 'direction', 'upbat', 'errorcodes'];

var wasSet = [];

function isSimple (value) {
return typeof value !== 'function' && typeof value !== 'object';
}

function nameFromKey (key, nameType) {
return nameType === 'env' ? _.snakeCase(key).toUpperCase() : key;
}

function eachSettingAs (nameType) {
function topKeys (setter, keys) {

function mapKeys (accessor, keys) {
_.forIn(keys, function each (value, key) {
if (isSimple(value)) {
var name = nameType === 'env' ? _.snakeCase(key).toUpperCase() : key;
var newValue = setter(name);
var newValue = accessor(nameFromKey(key, nameType));
if (newValue !== undefined) {
wasSet.push(key);
keys[key] = setter(name);
keys[key] = newValue;
}
}
});
}

return function allKeys (setter) {
topKeys(setter, settings);

//for env vars thresholds are at the top level, they aren't set on the client (yet)
if (nameType === 'env') {
topKeys(setter, settings.thresholds);
}
return function allKeys (accessor) {
mapKeys(accessor, settings);
mapKeys(accessor, settings.thresholds);
enableAndDisableFeatures(accessor, nameType);
};
}

function adjustShownPlugins ( ) {
//TODO: figure out something for some plugins to have them shown by default
if (settings.showPlugins !== '') {
settings.showPlugins += ' delta direction upbat';
if (settings.showRawbg === 'always' || settings.showRawbg === 'noise') {
settings.showPlugins += ' rawbg';
function enableAndDisableFeatures (accessor, nameType) {

function getAndPrepare (key) {
var raw = accessor(nameFromKey(key, nameType)) || '';
var cleaned = decodeURIComponent(raw).toLowerCase();
return cleaned ? cleaned.split(' ') : [];
}

function enableIf (feature, condition) {
if (condition) {
enable.push(feature);
}
}
}

function adjustAlarmTypes ( ) {
//if any threshold was set, and alarm types was not set default to simple
if (wasSet.indexOf('alarmTypes') === -1) {
var thresholdWasSet = _.findIndex(wasSet, function (name) {
return name.indexOf('bg') === 0;
function anyEnabled (features) {
return _.findIndex(features, function (feature) {
return enable.indexOf(feature) > -1;
}) > -1;
settings.alarmTypes = thresholdWasSet ? 'simple' : 'predict';
}

function prepareAlarmTypes ( ) {
var alarmTypes = _.filter(getAndPrepare('alarmTypes'), function onlyKnownTypes (type) {
return type === 'predict' || type === 'simple';
});

if (alarmTypes.length === 0) {
var thresholdWasSet = _.findIndex(wasSet, function (name) {
return name.indexOf('bg') === 0;
}) > -1;
alarmTypes = thresholdWasSet ? ['simple'] : ['predict'];
}

return alarmTypes;
}

var enable = getAndPrepare('enable');
var disable = getAndPrepare('disable');

settings.alarmTypes = prepareAlarmTypes();

//don't require pushover to be enabled to preserve backwards compatibility if there are extendedSettings for it
enableIf('pushover', accessor(nameFromKey('pushoverApiToken', nameType)));

enableIf('treatmentnotify', anyEnabled(['careportal', 'pushover', 'maker']));

_.each(settings.DEFAULT_FEATURES, function eachDefault (feature) {
enableIf(feature, enable.indexOf(feature) < 0);
});

//TODO: maybe get rid of ALARM_TYPES and only use enable?
enableIf('simplealarms', settings.alarmTypes.indexOf('simple') > -1);
enableIf('ar2', settings.alarmTypes.indexOf('predict') > -1);

if (disable.length > 0) {
console.info('disabling', disable);
}

//all enabled feature, without any that have been disabled
settings.enable = _.difference(enable, disable);

var thresholds = settings.thresholds;

thresholds.bgHigh = Number(thresholds.bgHigh);
thresholds.bgTargetTop = Number(thresholds.bgTargetTop);
thresholds.bgTargetBottom = Number(thresholds.bgTargetBottom);
thresholds.bgLow = Number(thresholds.bgLow);

verifyThresholds();
adjustShownPlugins();
}

function verifyThresholds() {
Expand Down Expand Up @@ -105,18 +158,15 @@ function init ( ) {
}
}

settings.processRawSettings = function processRawSettings ( ) {
var thresholds = settings.thresholds;

thresholds.bgHigh = Number(thresholds.bgHigh);
thresholds.bgTargetTop = Number(thresholds.bgTargetTop);
thresholds.bgTargetBottom = Number(thresholds.bgTargetBottom);
thresholds.bgLow = Number(thresholds.bgLow);

verifyThresholds();
adjustShownPlugins();
adjustAlarmTypes();
};
function adjustShownPlugins ( ) {
//TODO: figure out something for some plugins to have them shown by default
if (settings.showPlugins !== '') {
settings.showPlugins += ' delta direction upbat';
if (settings.showRawbg === 'always' || settings.showRawbg === 'noise') {
settings.showPlugins += ' rawbg';
}
}
}

function isEnabled (feature) {
var enabled = false;
Expand Down
2 changes: 1 addition & 1 deletion static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
<div class="bgButton ">
<span class="pill rawbg hidden"><em></em><label></label></span>
<span class="currentBG">---</span>
<span class="pill direction">-</span>
<span class="pill direction"></span>
</div>
<ul class="dropdown-menu" id="silenceBtn">
<li><a href="#" data-snooze-time="1800000" class="translate">Silence for 30 minutes</a></li>
Expand Down
6 changes: 4 additions & 2 deletions tests/api.status.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('Status REST api', function ( ) {
var api = require('../lib/api/');
before(function (done) {
var env = require('../env')( );
env.settings.enable = 'careportal rawbg';
env.settings.enable = ['careportal', 'rawbg'];
env.api_secret = 'this is my long pass phrase';
this.wares = require('../lib/middleware/')(env);
this.app = require('express')( );
Expand All @@ -26,7 +26,9 @@ describe('Status REST api', function ( ) {
.end(function (err, res) {
res.body.apiEnabled.should.equal(true);
res.body.careportalEnabled.should.equal(true);
res.body.settings.enable.should.equal('careportal rawbg');
res.body.settings.enable.length.should.equal(2);
res.body.settings.enable.should.containEql('careportal');
res.body.settings.enable.should.containEql('rawbg');
done( );
});
});
Expand Down
2 changes: 1 addition & 1 deletion tests/api.treatments.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('Treatment API', function ( ) {
before(function (done) {
process.env.API_SECRET = 'this is my long pass phrase';
self.env = require('../env')();
self.env.settings.enable = 'careportal';
self.env.settings.enable = ['careportal'];
this.wares = require('../lib/middleware/')(self.env);
self.app = require('express')();
self.app.enable('api');
Expand Down
10 changes: 0 additions & 10 deletions tests/env.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,6 @@ describe('env', function ( ) {
delete process.env.SCARYPLUGIN_DO_THING;
});

it('check if there are extended settings', function () {
var env = require('../env')();
env.hasExtendedSetting('PUSHOVER', {'PUSHOVER_API_TOKEN': 'abc12345'}).should.equal(true);
});

it('check if there are extended settings for azure', function () {
var env = require('../env')();
env.hasExtendedSetting('PUSHOVER', {'CUSTOMCONNSTR_PUSHOVER_API_TOKEN': 'abc12345'}).should.equal(true);
});

it('add pushover to enable if one of the env vars is set', function () {
process.env.PUSHOVER_API_TOKEN = 'abc12345';

Expand Down
2 changes: 1 addition & 1 deletion tests/pebble.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ describe('Pebble Endpoint with Raw and IOB', function ( ) {
before(function (done) {
ctx.data.devicestatus.uploaderBattery = 100;
var envRaw = require('../env')( );
envRaw.settings.enable = 'rawbg iob';
envRaw.settings.enable = ['rawbg', 'iob'];
this.appRaw = require('express')( );
this.appRaw.enable('api');
this.appRaw.use('/pebble', pebbleRaw(envRaw, ctx));
Expand Down

0 comments on commit 1779e61

Please sign in to comment.