Skip to content

Commit 3ffc005

Browse files
Merge pull request #4587 from jasoncalabrese/override-mode
feat: Override mode plugin
2 parents 1c4bc54 + a4f9b52 commit 3ffc005

File tree

5 files changed

+120
-21
lines changed

5 files changed

+120
-21
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Community maintained fork of the
9696
- [`pump` (Pump Monitoring)](#pump-pump-monitoring)
9797
- [`openaps` (OpenAPS)](#openaps-openaps)
9898
- [`loop` (Loop)](#loop-loop)
99+
- [`override` (Override Mode)](#override-override-mode)
99100
- [`xdrip-js` (xDrip-js)](#xdrip-js-xdrip-js)
100101
- [`alexa` (Amazon Alexa)](#alexa-amazon-alexa)
101102
- [`speech` (Speech)](#speech-speech)
@@ -485,6 +486,10 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs.htm
485486
* `LOOP_URGENT` (`60`) - The number of minutes since the last loop that needs to be exceeded before an urgent alarm is triggered
486487
* Add `loop` to `SHOW_FORECAST` to show forecasted BG.
487488

489+
##### `override` (Override Mode)
490+
Additional monitoring for DIY automated insulin delivery systems to display real-time overrides such as Eating Soon or Exercise Mode:
491+
* Requires `DEVICESTATUS_ADVANCED="true"` to be set
492+
488493
##### `xdrip-js` (xDrip-js)
489494
Integrated xDrip-js monitoring, uses these extended settings:
490495
* Requires `DEVICESTATUS_ADVANCED="true"` to be set

lib/plugins/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ function init (ctx) {
3737
, require('./openaps')(ctx)
3838
, require('./xdrip-js')(ctx)
3939
, require('./loop')(ctx)
40+
, require('./override')(ctx)
4041
, require('./boluswizardpreview')(ctx)
4142
, require('./cannulaage')(ctx)
4243
, require('./sensorage')(ctx)

lib/plugins/loop.js

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ function init(ctx) {
113113
}
114114
}
115115

116+
function assignLastOverride (status) {
117+
var override = status.override;
118+
if (override && override.timestamp) {
119+
override.moment = moment(override.timestamp);
120+
if (!result.lastOverride || override.moment.isAfter(result.lastOverride.moment)) {
121+
result.lastOverride = override;
122+
}
123+
}
124+
}
125+
116126
function assignLastOkMoment (loopStatus) {
117127
if (!loopStatus.failureReason && (!result.lastOkMoment || loopStatus.moment.isAfter(result.lastOkMoment))) {
118128
result.lastOkMoment = loopStatus.moment;
@@ -126,6 +136,7 @@ function init(ctx) {
126136
assignLastEnacted(loopStatus);
127137
assignLastLoop(loopStatus);
128138
assignLastPredicted(loopStatus);
139+
assignLastOverride(status);
129140
assignLastOkMoment(loopStatus);
130141
}
131142
});
@@ -299,27 +310,28 @@ function init(ctx) {
299310
if (prop.lastLoop && prop.lastLoop.predicted) {
300311
var predictedBGvalues = prop.lastLoop.predicted.values;
301312
var eventualBG = predictedBGvalues[predictedBGvalues.length-1];
302-
var maxBG = Math.max.apply(null,predictedBGvalues);
303-
var minBG = Math.min.apply(null,predictedBGvalues);
304-
var eventualBGscaled = sbx.settings.units === 'mmol' ?
313+
var maxBG = Math.max.apply(null,predictedBGvalues);
314+
var minBG = Math.min.apply(null,predictedBGvalues);
315+
var eventualBGscaled = sbx.settings.units === 'mmol' ?
305316
sbx.roundBGToDisplayFormat(sbx.scaleMgdl(eventualBG)) : eventualBG;
306317
var maxBGscaled = sbx.settings.units === 'mmol' ?
307318
sbx.roundBGToDisplayFormat(sbx.scaleMgdl(maxBG)) : maxBG;
308319
var minBGscaled = sbx.settings.units === 'mmol' ?
309320
sbx.roundBGToDisplayFormat(sbx.scaleMgdl(minBG)) : minBG;
321+
310322
valueParts = valueParts.concat([
311323
', Predicted Min-Max BG: '
312-
, minBGscaled
313-
, '-'
314-
, maxBGscaled
324+
, minBGscaled
325+
, '-'
326+
, maxBGscaled
315327
,', Eventual BG: '
316-
, eventualBGscaled
328+
, eventualBGscaled
317329
]);
318330
}
319-
331+
320332
return valueParts;
321333
}
322-
334+
323335
function concatRecommendedBolus (valueParts) {
324336
if (prop.lastLoop && prop.lastLoop.recommendedBolus) {
325337
var recommendedBolus = prop.lastLoop.recommendedBolus;
@@ -331,8 +343,7 @@ function init(ctx) {
331343

332344
return valueParts;
333345
}
334-
335-
346+
336347
function getForecastPoints ( ) {
337348
var points = [ ];
338349

@@ -397,12 +408,23 @@ function init(ctx) {
397408
loopName = prop.lastLoop.name;
398409
}
399410

411+
var eventualBGValue = '';
412+
if (prop.lastLoop && prop.lastLoop.predicted) {
413+
var predictedBGvalues = prop.lastLoop.predicted.values;
414+
var eventualBG = predictedBGvalues[predictedBGvalues.length-1];
415+
if (sbx.settings.units === 'mmol') {
416+
eventualBG = sbx.roundBGToDisplayFormat(sbx.scaleMgdl(eventualBG));
417+
}
418+
eventualBGValue = ' ↝ ' + eventualBG;
419+
}
420+
400421
var label = loopName + ' ' + prop.display.symbol;
401422

402-
var lastLoopMoment = prop.lastLoop ? prop.lastLoop.moment : null;
423+
var lastLoopValue = prop.lastLoop ?
424+
utils.timeFormat(prop.lastLoop.moment, sbx) + eventualBGValue : null;
403425

404426
sbx.pluginBase.updatePillText(loop, {
405-
value: utils.timeFormat(lastLoopMoment, sbx)
427+
value: lastLoopValue
406428
, label: label
407429
, info: info
408430
, pillClass: statusClass(prop, prefs, sbx)

lib/plugins/override.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
function init() {
4+
var override = {
5+
name: 'override'
6+
, label: 'Override'
7+
, pluginType: 'pill-status'
8+
};
9+
10+
override.isActive = function isActive(overrideStatus, sbx) {
11+
12+
if (!overrideStatus) {
13+
return false;
14+
} else {
15+
var endMoment = overrideStatus.duration ? overrideStatus.moment.clone().add(overrideStatus.duration, 'seconds') : null;
16+
overrideStatus.endMoment = endMoment;
17+
return overrideStatus.active && (!endMoment || endMoment.isAfter(sbx.time));
18+
}
19+
20+
};
21+
22+
override.updateVisualisation = function updateVisualisation (sbx) {
23+
var lastOverride = sbx.properties.loop.lastOverride;
24+
var info = [ ];
25+
var label = '';
26+
var isActive = override.isActive(lastOverride, sbx);
27+
28+
if (isActive) {
29+
if (lastOverride.currentCorrectionRange) {
30+
var max = lastOverride.currentCorrectionRange.maxValue;
31+
var min = lastOverride.currentCorrectionRange.minValue;
32+
33+
if (sbx.settings.units === 'mmol') {
34+
max = sbx.roundBGToDisplayFormat(sbx.scaleMgdl(max));
35+
min = sbx.roundBGToDisplayFormat(sbx.scaleMgdl(min));
36+
}
37+
38+
if (lastOverride.currentCorrectionRange.minValue === lastOverride.currentCorrectionRange.maxValue) {
39+
label += 'BG Target: ' + min;
40+
} else {
41+
label += 'BG Targets: ' + min + ':' + max;
42+
}
43+
}
44+
if ((lastOverride.multiplier || lastOverride.multiplier === 0) && lastOverride.multiplier !== 1) {
45+
var multiplier = (lastOverride.multiplier * 100).toFixed(0);
46+
label += ' | O: ' + multiplier + '%';
47+
}
48+
}
49+
50+
var endOverrideValue = lastOverride && lastOverride.endMoment ?
51+
'⇥ ' + lastOverride.endMoment.format('LT') : (lastOverride ? '∞' : '');
52+
53+
sbx.pluginBase.updatePillText(override, {
54+
value: endOverrideValue
55+
, label: label
56+
, info: info
57+
, hide: !isActive
58+
});
59+
60+
};
61+
62+
return override;
63+
64+
}
65+
66+
67+
module.exports = init;

static/css/main.css

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,13 @@ a, a:visited, a:link {
119119
font-family: 'Ubuntu', Verdana, Helvetica, Arial, sans-serif;
120120
vertical-align: middle;
121121
clear: both;
122+
white-space: nowrap;
122123
}
123124

124125
.bgStatus {
125126
float: right;
126127
text-align: center;
127-
white-space: nowrap;
128+
white-space: normal;
128129
padding-right: 20px;
129130
}
130131

@@ -240,14 +241,16 @@ a, a:visited, a:link {
240241
.status {
241242
color: #808080;
242243
font-size: 100px;
244+
white-space: normal;
243245
}
244246
.statusBox {
245247
text-align: center;
246-
width: 250px;
248+
width: 40%;
247249
}
248250
.statusPills {
249251
font-size: 20px;
250252
line-height: 23px;
253+
margin-left: 5px;
251254
}
252255

253256
.loading .statusPills {
@@ -397,7 +400,7 @@ a, a:visited, a:link {
397400
margin: 4px;
398401
padding: 0;
399402
color: #808080;
400-
width: 250px;
403+
width: 40%;
401404
text-align: center;
402405
}
403406

@@ -474,6 +477,7 @@ a, a:visited, a:link {
474477

475478
.focus-range {
476479
margin: 0;
480+
width: 50%;
477481
}
478482

479483
.focus-range li {
@@ -491,7 +495,7 @@ a, a:visited, a:link {
491495

492496
@media (max-width: 750px) {
493497
.bgStatus {
494-
width: 240px;
498+
width: 50%;
495499
padding: 0 0 20px 0;
496500
}
497501

@@ -503,6 +507,10 @@ a, a:visited, a:link {
503507
font-size: 12px;
504508
}
505509

510+
.statusBox {
511+
width: auto;
512+
}
513+
506514
.bgStatus .currentBG {
507515
font-size: 70px;
508516
line-height: 60px;
@@ -741,10 +749,6 @@ a, a:visited, a:link {
741749
display: block;
742750
}
743751

744-
.statusBox {
745-
width: 220px;
746-
}
747-
748752
.statusPills {
749753
display: inline;
750754
margin-left: auto;

0 commit comments

Comments
 (0)