Document Version: 1.0
Last Updated: January 2026
Scope: Plugin system design, extension points, Pebble integration, plugin inventory
Nightscout's plugin system provides extensible data processing, visualization, and alerting capabilities. This audit documents the plugin architecture, available plugins, and modernization opportunities.
| Metric | Value |
|---|---|
| Total Plugins | 38 |
| Client Default Plugins | 24 |
| Server Default Plugins | 21 |
| Plugin Types | 6 |
| Extension Points | 5 |
Location: lib/plugins/pluginbase.js
All plugins inherit from a common base that provides:
- Access to translation system
- Access to moment.js for date handling
- Access to utility functions
Location: lib/plugins/index.js
Registration Flow:
// During boot
ctx.plugins = require('../plugins')({
settings: env.settings,
language: ctx.language,
levels: ctx.levels,
moment: ctx.moment
}).registerServerDefaults();Client vs Server Plugins:
- Server plugins: Run on Node.js, check notifications, server-side processing
- Client plugins: Run in browser, update visualizations, UI interactions
Each plugin receives a context object (ctx) with:
| Property | Description |
|---|---|
settings |
Application settings |
language |
Translation functions |
levels |
Alarm level definitions |
moment |
Date/time library |
notifications |
Notification system access |
ddata |
Current data snapshot |
Display primary values in the main UI pill area.
| Plugin | Purpose |
|---|---|
bgnow |
Current blood glucose value |
rawbg |
Raw/unfiltered glucose value |
Display status indicators and secondary information.
| Plugin | Purpose |
|---|---|
timeago |
Time since last reading |
upbat |
Uploader battery status |
direction |
Glucose trend arrow |
Provide predictions and trend analysis.
| Plugin | Purpose |
|---|---|
ar2 |
Auto-regressive prediction |
loop |
Loop system predictions |
openaps |
OpenAPS predictions |
Generate historical analysis reports.
| Plugin | Purpose |
|---|---|
dailystats |
Daily statistics |
glucosedistribution |
Time in range analysis |
hourlystats |
Hourly breakdown |
percentile |
Percentile charts |
Generate alarms and notifications.
| Plugin | Purpose |
|---|---|
simplealarms |
Basic high/low alarms |
ar2 |
Predictive alarms |
treatmentnotify |
Treatment notifications |
errorcodes |
Error condition alerts |
Process and calculate derived values.
| Plugin | Purpose |
|---|---|
iob |
Insulin on board calculation |
cob |
Carbs on board calculation |
basalprofile |
Basal rate display |
boluswizardpreview |
Bolus calculator |
function init (ctx) {
var plugin = {
name: 'myplugin',
label: 'My Plugin',
pluginType: 'pill-status'
};
// Plugin-specific initialization
return plugin;
}
module.exports = init;| Method | When Called | Purpose |
|---|---|---|
setProperties(sbx) |
After data load | Calculate derived values |
checkNotifications(sbx) |
After properties set | Generate alarms |
updateVisualisation(sbx) |
After UI render | Update UI elements |
visualizeAlarm(sbx, alarm) |
On alarm | Custom alarm display |
getEventTypes(sbx) |
On request | Return supported events |
Location: lib/sandbox.js
The sandbox provides a safe execution context for plugins:
sbx = {
data: ctx.ddata, // Current data
settings: env.settings, // Settings
pluginBase: plugins.base, // Base utilities
// Helper methods
scaleMgdl: function(value) { },
roundBGToDisplayFormat: function(value) { },
// Properties set by plugins
properties: {},
// Notification methods
notifications: {
requestNotify: function(notify) { },
requestSnooze: function(snooze) { }
}
};| Plugin | Type | Description | Settings |
|---|---|---|---|
bgnow |
pill-primary | Current BG display | None |
rawbg |
pill-primary | Raw BG values | RAWBG_ENABLE |
direction |
pill-status | Trend arrows | None |
timeago |
pill-status | Time since reading | TIMEAGO_ENABLE |
upbat |
pill-status | Uploader battery | UPBAT_ENABLE |
ar2 |
forecast | Prediction algorithm | AR2_ENABLE |
errorcodes |
notification | CGM error codes | ERRORCODES_ENABLE |
iob |
data | Insulin on board | IOB_ENABLE |
cob |
data | Carbs on board | COB_ENABLE |
careportal |
UI | Treatment entry | CAREPORTAL_ENABLE |
pump |
pill-status | Pump status | PUMP_ENABLE |
openaps |
forecast | OpenAPS status | OPENAPS_ENABLE |
xdripjs |
data | xDrip+ status | XDRIPJS_ENABLE |
loop |
forecast | Loop status | LOOP_ENABLE |
override |
data | Override status | OVERRIDE_ENABLE |
boluswizardpreview |
data | Bolus calculator | BWP_ENABLE |
cannulaage |
pill-status | Cannula age | CAGE_ENABLE |
sensorage |
pill-status | Sensor age | SAGE_ENABLE |
insulinage |
pill-status | Insulin age | IAGE_ENABLE |
batteryage |
pill-status | Battery age | BAGE_ENABLE |
basalprofile |
data | Basal rate display | BASAL_ENABLE |
bolus |
settings | Bolus settings | None |
boluscalc |
UI | Bolus calculator | BOLUSCALC_ENABLE |
profile |
settings | Profile settings | None |
speech |
UI | Voice announcements | SPEECH_ENABLE |
dbsize |
admin | Database size | DBSIZE_ENABLE |
Server-only plugins (subset of client + server-specific):
| Plugin | Additional Notes |
|---|---|
simplealarms |
Server-only: basic threshold alarms |
treatmentnotify |
Server-only: treatment notifications |
runtimestate |
Server-only: runtime state tracking |
| Plugin | Location | Purpose |
|---|---|---|
pushover |
lib/plugins/pushover.js |
Pushover notifications |
maker |
lib/plugins/maker.js |
IFTTT integration |
alexa |
lib/plugins/alexa.js |
Alexa skill |
googlehome |
lib/plugins/googlehome.js |
Google Home actions |
bridge |
lib/plugins/bridge.js |
Dexcom Share bridge |
mmconnect |
lib/plugins/mmconnect.js |
Medtronic CareLink |
Location: lib/plugins/bgnow.js
Purpose: Calculate and display current blood glucose
Properties Set:
sbx.properties.bgnow = {
mean: averageBG, // Average of recent readings
last: latestReading, // Most recent reading
sgvs: recentReadings, // Last few readings
buckets: timeBuckets // Readings grouped by time
};Location: lib/plugins/ar2.js
Purpose: Auto-regressive prediction algorithm
Algorithm:
- Takes last 2 readings
- Applies AR(2) coefficients
- Projects 5, 10, 15, 20, 25, 30 minute values
- Calculates probability of crossing thresholds
Alarm Logic:
if (probability > URGENT_THRESHOLD) {
// Request urgent alarm
} else if (probability > WARN_THRESHOLD) {
// Request warning alarm
}Location: lib/plugins/iob.js
Purpose: Calculate insulin on board
Calculation:
- Uses DIA (Duration of Insulin Action) from profile
- Sums active insulin from recent boluses
- Applies decay curve
Location: lib/plugins/cob.js
Purpose: Calculate carbs on board
Calculation:
- Uses carb absorption rate from profile
- Tracks unabsorbed carbs from recent meals
- Considers carb ratio and absorption patterns
Location: lib/plugins/loop.js, lib/plugins/openaps.js
Purpose: Display closed-loop system status
Data Sources:
- Device status entries from Loop/OpenAPS
- Predicted glucose values
- Enacted temp basals
- IOB/COB from loop calculations
Location: lib/server/pebble.js
Endpoint: GET /pebble
Response Format:
{
"bgs": [
{
"sgv": "120",
"trend": 4,
"direction": "Flat",
"datetime": 1595000000000,
"filtered": 124048,
"unfiltered": 118880,
"noise": 1,
"battery": "100"
}
],
"cals": [],
"status": [
{
"now": 1595000000000
}
]
}var DIRECTIONS = {
NONE: 0,
DoubleUp: 1,
SingleUp: 2,
FortyFiveUp: 3,
Flat: 4,
FortyFiveDown: 5,
SingleDown: 6,
DoubleDown: 7,
'NOT COMPUTABLE': 8,
'RATE OUT OF RANGE': 9
};- Trend arrow display
- Battery status
- Time since last reading
- Delta (change since last reading)
- Optional: IOB, COB, predictions
Location: lib/report_plugins/
| Plugin | File | Reports Generated |
|---|---|---|
| dailystats | dailystats.js |
Daily average, min, max, std dev |
| glucosedistribution | glucosedistribution.js |
Time in range percentages |
| hourlystats | hourlystats.js |
Hour-by-hour breakdown |
| percentile | percentile.js |
Percentile overlay chart |
var reportPlugin = {
name: 'dailystats',
label: 'Daily Stats',
pluginType: 'report'
};
reportPlugin.html = function(client) {
// Return HTML template
};
reportPlugin.css = 'CSS styles here';
reportPlugin.report = function(datastorage, sorteddaystoshow, options) {
// Generate report data
};Environment Variable:
ENABLE=careportal iob cob openaps pump
Programmatic:
env.settings.enable = ['careportal', 'iob', 'cob'];Plugins can have extended settings:
PUMP_FIELDS=clock reservoir battery
IOB_FRAC=0.5
Access in Plugin:
var settings = sbx.extendedSettings;
var pumpFields = settings.pump.fields;No formal settings schema exists. Each plugin defines its own settings interpretation.
Recommendation: Add JSON Schema for plugin settings validation.
Add new calculations:
plugin.setProperties = function(sbx) {
sbx.properties.myplugin = {
value: calculateValue(sbx.data)
};
};Add new alarm types:
plugin.checkNotifications = function(sbx) {
if (condition) {
sbx.notifications.requestNotify({
level: sbx.levels.WARN,
title: 'My Alarm',
message: 'Description',
plugin: plugin
});
}
};Add UI elements:
plugin.updateVisualisation = function(sbx) {
$('#my-element').text(sbx.properties.myplugin.value);
};Add treatment types:
plugin.getEventTypes = function(sbx) {
return [{
val: 'MyEvent',
name: 'My Custom Event'
}];
};Add Alexa/Google Home intents:
plugin.virtAsst = {
intentHandlers: [{
intent: 'MyIntent',
handler: function(callback, slots, sbx) {
callback('Response text', 'Card title', 'Card content');
}
}]
};| Issue | Impact | Recommendation |
|---|---|---|
| No plugin isolation | Security risk | Add sandboxing |
| Global state mutation | Race conditions | Immutable data patterns |
| No async support | Blocking operations | Add async lifecycle |
| Tight DOM coupling | Testing difficulty | Decouple from DOM |
| No plugin versioning | Compatibility issues | Add version metadata |
| Issue | Impact | Recommendation |
|---|---|---|
| No TypeScript support | Type errors | Add TypeScript definitions |
| Limited documentation | Learning curve | Document plugin API |
| No plugin template | Slow onboarding | Create plugin generator |
| No testing utilities | Quality issues | Add testing helpers |
-
Plugin Isolation:
- Run plugins in separate contexts
- Add capability-based permissions
- Implement resource limits
-
Async Support:
plugin.setProperties = async function(sbx) { const data = await fetchExternalData(); sbx.properties.myplugin = data; };
-
Plugin Manifest:
{ "name": "myplugin", "version": "1.0.0", "requires": ["bgnow"], "permissions": ["notifications"], "settings": { "threshold": { "type": "number", "default": 100 } } } -
Hot Reloading:
- Enable plugin updates without restart
- Add plugin state serialization
- Limited unit tests in
tests/directory - Manual testing predominant
- No integration test framework
Unit Test Template:
describe('myplugin', function() {
var ctx, sbx;
beforeEach(function() {
ctx = require('./ctx-mock')();
sbx = require('./sbx-mock')(ctx);
});
it('should calculate value correctly', function() {
var plugin = require('../lib/plugins/myplugin')(ctx);
plugin.setProperties(sbx);
sbx.properties.myplugin.value.should.equal(expected);
});
});