From 423d1c1e42663e23002f8a3023d095174e793155 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Fri, 10 Apr 2015 01:55:30 -0700 Subject: [PATCH 1/4] first pass at client side alarm for stale data --- README.md | 2 ++ env.js | 6 +++++- lib/websocket.js | 11 +++++++--- static/index.html | 2 ++ static/js/client.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++----- static/js/ui-utils.js | 10 ++++++++- 6 files changed, 79 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 07bf166f5..b0d0c0820 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,8 @@ Use the [autoconfigure tool][autoconfigure] to sync an uploader to your config. * `ALARM_HIGH` (`on`) - possible values `on` or `off` * `ALARM_LOW` (`on`) - possible values `on` or `off` * `ALARM_URGENT_LOW` (`on`) - possible values `on` or `off` + * `ALARM_TIMEAGO_WARN` (`on`) - possible values `on` or `off` + * `ALARM_TIMEAGO_URGENT` (`on`) - possible values `on` or `off` ## Setting environment variables diff --git a/env.js b/env.js index c8be09fb2..12727dfc3 100644 --- a/env.js +++ b/env.js @@ -58,6 +58,8 @@ function config ( ) { , 'alarmHigh': true , 'alarmLow': true , 'alarmUrgentLow': true + , 'alarmTimeAgoWarn': true + , 'alarmTimeAgoUrgent': true , 'language': 'en' // not used yet } ; @@ -74,7 +76,9 @@ function config ( ) { env.defaults.alarmHigh = readENV('ALARM_HIGH', env.defaults.alarmHigh); env.defaults.alarmLow = readENV('ALARM_LOW', env.defaults.alarmLow); env.defaults.alarmUrgentLow = readENV('ALARM_URGENT_LOW', env.defaults.alarmUrgentLow); - + env.defaults.alarmTimeAgoWarn = readENV('ALARM_TIMEAGO_WARN', env.defaults.alarmTimeAgoWarn); + env.defaults.alarmTimeAgoUrgent = readENV('ALARM_TIMEAGO_URGENT', env.defaults.alarmTimeAgoUrgent); + //console.log(JSON.stringify(env.defaults)); env.SSL_KEY = readENV('SSL_KEY'); diff --git a/lib/websocket.js b/lib/websocket.js index 760aae25b..c3da8be29 100644 --- a/lib/websocket.js +++ b/lib/websocket.js @@ -109,9 +109,14 @@ var alarms = { }; function ackAlarm(alarmType, silenceTime) { - alarms[alarmType].lastAckTime = new Date().getTime(); - alarms[alarmType].silenceTime = silenceTime ? silenceTime : FORTY_MINUTES; - delete alarms[alarmType].lastEmitTime; + var alarm = alarms[alarmType]; + if (!alarm) { + console.warn('Got an ack for an unknown alarm time'); + return; + } + alarm.lastAckTime = new Date().getTime(); + alarm.silenceTime = silenceTime ? silenceTime : FORTY_MINUTES; + delete alarm.lastEmitTime; } //should only be used when auto acking the alarms after going back in range or when an error corrects diff --git a/static/index.html b/static/index.html index 4f77b714d..a2bf5c78f 100644 --- a/static/index.html +++ b/static/index.html @@ -82,6 +82,8 @@

Nightscout

+
+
Night Mode
diff --git a/static/js/client.js b/static/js/client.js index c0a2dab59..ba767eef3 100644 --- a/static/js/client.js +++ b/static/js/client.js @@ -26,8 +26,8 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; , HOUR_IN_SECS = 3600 , DAY_IN_SECS = 86400 , WEEK_IN_SECS = 604800 - , MINUTES_SINCE_LAST_UPDATE_WARN = 10 - , MINUTES_SINCE_LAST_UPDATE_URGENT = 20; + , MINUTES_SINCE_LAST_UPDATE_WARN = 15 + , MINUTES_SINCE_LAST_UPDATE_URGENT = 30; var socket , isInitialData = false @@ -43,6 +43,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; , now = Date.now() , data = [] , foucusRangeMS = THREE_HOURS_MS + , clientAlarms = {} , audio = document.getElementById('audio') , alarmInProgress = false , currentAlarmType = null @@ -1186,13 +1187,21 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; }); $('#container').removeClass('alarming'); - brushed(true); // only emit ack if client invoke by button press if (isClient) { - socket.emit('ack', currentAlarmType || 'alarm', silenceTime); - brushed(false); + if (isTimeAgoAlarmType(currentAlarmType)) { + $('#container').removeClass('alarming-timeago'); + var alarm = getClientAlarm(currentAlarmType); + alarm.lastAckTime = Date.now(); + alarm.silenceTime = silenceTime; + console.info('time ago alarm (' + currentAlarmType + ', not acking to server'); + } else { + socket.emit('ack', currentAlarmType || 'alarm', silenceTime); + } } + + brushed(false); } function timeAgo(time) { @@ -1458,6 +1467,35 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; $('#currentTime').text(formatTime(dateTime, true)).css('text-decoration', ''); } + function getClientAlarm(type) { + var alarm = clientAlarms[type]; + if (!alarm) { + alarm = { type: type }; + clientAlarms[type] = alarm; + } + return alarm; + } + + function isTimeAgoAlarmType(alarmType) { + return alarmType == 'warnTimeAgo' || alarmType == 'urgentTimeAgo'; + } + + function checkTimeAgoAlarm(ago) { + var level = ago.status + , alarm = getClientAlarm(level + 'TimeAgo'); + + if (!alarmingNow() && Date.now() >= (alarm.lastAckTime || 0) + (alarm.silenceTime || 0)) { + currentAlarmType = alarm.type; + console.info('generating timeAgoAlarm', alarm.type); + $('#container').addClass('alarming-timeago'); + if (level == 'warn') { + generateAlarm(alarmSound); + } else { + generateAlarm(urgentAlarmSound); + } + } + } + function updateTimeAgo() { var lastEntry = $('#lastEntry') , time = latestSGV ? new Date(latestSGV.x).getTime() : -1 @@ -1471,6 +1509,16 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; updateTitle(); } + if ( + (browserSettings.alarmTimeAgoWarn && ago.status == 'warn') + || (browserSettings.alarmTimeAgoUrgent && ago.status == 'urgent')) { + checkTimeAgoAlarm(ago); + } + + if (alarmingNow() && ago.status == 'current' && isTimeAgoAlarmType(currentAlarmType)) { + stopAlarm(true, ONE_MIN_IN_MS); + } + if (retroMode || !ago.value) { lastEntry.find('em').hide(); } else { diff --git a/static/js/ui-utils.js b/static/js/ui-utils.js index 3736686a2..464f60014 100644 --- a/static/js/ui-utils.js +++ b/static/js/ui-utils.js @@ -28,6 +28,8 @@ function getBrowserSettings(storage) { 'alarmHigh': storage.get('alarmHigh'), 'alarmLow': storage.get('alarmLow'), 'alarmUrgentLow': storage.get('alarmUrgentLow'), + 'alarmTimeAgoWarn': storage.get('alarmTimeAgoWarn'), + 'alarmTimeAgoUrgent': storage.get('alarmTimeAgoUrgent'), 'nightMode': storage.get('nightMode'), 'showRawbg': storage.get('showRawbg'), 'customTitle': storage.get('customTitle'), @@ -47,10 +49,14 @@ function getBrowserSettings(storage) { json.alarmHigh = setDefault(json.alarmHigh, app.defaults.alarmHigh); json.alarmLow = setDefault(json.alarmLow, app.defaults.alarmLow); json.alarmUrgentLow = setDefault(json.alarmUrgentLow, app.defaults.alarmUrgentLow); + json.alarmTimeAgoWarn = setDefault(json.alarmTimeAgoWarn, app.defaults.alarmTimeAgoWarn); + json.alarmTimeAgoUrgent = setDefault(json.alarmTimeAgoUrgent, app.defaults.alarmTimeAgoUrgent); $('#alarm-urgenthigh-browser').prop('checked', json.alarmUrgentHigh).next().text('Urgent High Alarm' + appendThresholdValue(app.thresholds.bg_high)); $('#alarm-high-browser').prop('checked', json.alarmHigh).next().text('High Alarm' + appendThresholdValue(app.thresholds.bg_target_top)); $('#alarm-low-browser').prop('checked', json.alarmLow).next().text('Low Alarm' + appendThresholdValue(app.thresholds.bg_target_bottom)); $('#alarm-urgentlow-browser').prop('checked', json.alarmUrgentLow).next().text('Urgent Low Alarm' + appendThresholdValue(app.thresholds.bg_low)); + $('#alarm-timeagowarn-browser').prop('checked', json.alarmTimeAgoWarn); + $('#alarm-timeagourgent-browser').prop('checked', json.alarmTimeAgoUrgent); json.nightMode = setDefault(json.nightMode, app.defaults.nightMode); $('#nightmode-browser').prop('checked', json.nightMode); @@ -351,6 +357,8 @@ $('#save').click(function(event) { 'alarmHigh': $('#alarm-high-browser').prop('checked'), 'alarmLow': $('#alarm-low-browser').prop('checked'), 'alarmUrgentLow': $('#alarm-urgentlow-browser').prop('checked'), + 'alarmTimeAgoWarn': $('#alarm-timeagowarn-browser').prop('checked'), + 'alarmTimeAgoUrgent': $('#alarm-timeagourgent-browser').prop('checked'), 'nightMode': $('#nightmode-browser').prop('checked'), 'showRawbg': $('input:radio[name=show-rawbg]:checked').val(), 'customTitle': $('input#customTitle').prop('value'), @@ -365,7 +373,7 @@ $('#save').click(function(event) { $('#useDefaults').click(function(event) { //remove all known settings, since there might be something else is in localstorage - var settings = ['units', 'alarmUrgentHigh', 'alarmHigh', 'alarmLow', 'alarmUrgentLow', 'nightMode', 'showRawbg', 'customTitle', 'theme', 'timeFormat']; + var settings = ['units', 'alarmUrgentHigh', 'alarmHigh', 'alarmLow', 'alarmUrgentLow', 'alarmTimeAgoWarn', 'alarmTimeAgoUrgent', 'nightMode', 'showRawbg', 'customTitle', 'theme', 'timeFormat']; settings.forEach(function(setting) { browserStorage.remove(setting); }); From 91a424f647d9a07993284849962628a36988f327 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Wed, 15 Apr 2015 23:33:34 -0700 Subject: [PATCH 2/4] added env vars with client overrides for timeago warn/urgent mins; make it more clear what type of alarm has been triggered --- README.md | 2 ++ env.js | 4 ++++ static/css/drawer.css | 4 ++++ static/css/main.css | 18 ++++++++++++++++++ static/index.html | 12 ++++++++++-- static/js/client.js | 8 +++----- static/js/ui-utils.js | 10 +++++++++- 7 files changed, 50 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b0d0c0820..a635bc91d 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,9 @@ Use the [autoconfigure tool][autoconfigure] to sync an uploader to your config. * `ALARM_LOW` (`on`) - possible values `on` or `off` * `ALARM_URGENT_LOW` (`on`) - possible values `on` or `off` * `ALARM_TIMEAGO_WARN` (`on`) - possible values `on` or `off` + * `ALARM_TIMEAGO_WARN_MINS` (`15`) - minutes since the last reading to trigger a warning * `ALARM_TIMEAGO_URGENT` (`on`) - possible values `on` or `off` + * `ALARM_TIMEAGO_URGENT_MINS` (`30`) - minutes since the last reading to trigger a urgent alarm ## Setting environment variables diff --git a/env.js b/env.js index e32ebb810..5c06e5c9a 100644 --- a/env.js +++ b/env.js @@ -69,7 +69,9 @@ function config ( ) { , 'alarmLow': true , 'alarmUrgentLow': true , 'alarmTimeAgoWarn': true + , 'alarmTimeAgoWarnMins': 15 , 'alarmTimeAgoUrgent': true + , 'alarmTimeAgoUrgentMins': 30 , 'language': 'en' // not used yet } ; @@ -87,7 +89,9 @@ function config ( ) { env.defaults.alarmLow = readENV('ALARM_LOW', env.defaults.alarmLow); env.defaults.alarmUrgentLow = readENV('ALARM_URGENT_LOW', env.defaults.alarmUrgentLow); env.defaults.alarmTimeAgoWarn = readENV('ALARM_TIMEAGO_WARN', env.defaults.alarmTimeAgoWarn); + env.defaults.alarmTimeAgoWarnMins = readENV('ALARM_TIMEAGO_WARN_MINS', env.defaults.alarmTimeAgoWarnMins); env.defaults.alarmTimeAgoUrgent = readENV('ALARM_TIMEAGO_URGENT', env.defaults.alarmTimeAgoUrgent); + env.defaults.alarmTimeAgoUrgentMins = readENV('ALARM_TIMEAGO_URGENT_MINS', env.defaults.alarmTimeAgoUrgentMins); //console.log(JSON.stringify(env.defaults)); diff --git a/static/css/drawer.css b/static/css/drawer.css index 01e6905c6..6368e858e 100644 --- a/static/css/drawer.css +++ b/static/css/drawer.css @@ -85,6 +85,10 @@ input[type=number]:invalid { display: block; } +input.timeago-mins { + width: 40px; +} + #eventTime { padding-bottom: 15px; } diff --git a/static/css/main.css b/static/css/main.css index e30a555e6..0d2e652d4 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -109,10 +109,28 @@ body { color: red !important; } +.alarming-timeago .timeOther .pill.warn { + border-color: yellow; +} +.alarming-timeago .timeOther .pill.warn label { + background: yellow; +} + +.alarming-timeago .timeOther .pill.urgent { + border-color: red; +} +.alarming-timeago .timeOther .pill.urgent label { + background: red; +} + .bgStatus.current .currentBG { text-decoration: none; } +.alarming-timeago .bgStatus.current .currentBG { + text-decoration: line-through; +} + .time { color: #808080; font-size: 100px; diff --git a/static/index.html b/static/index.html index a2bf5c78f..538339c89 100644 --- a/static/index.html +++ b/static/index.html @@ -82,8 +82,16 @@

Nightscout

-
-
+
+ + + +
+
+ + + +
Night Mode
diff --git a/static/js/client.js b/static/js/client.js index 01996f83f..dd35bd3c6 100644 --- a/static/js/client.js +++ b/static/js/client.js @@ -25,9 +25,7 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; , MINUTE_IN_SECS = 60 , HOUR_IN_SECS = 3600 , DAY_IN_SECS = 86400 - , WEEK_IN_SECS = 604800 - , MINUTES_SINCE_LAST_UPDATE_WARN = 15 - , MINUTES_SINCE_LAST_UPDATE_URGENT = 30; + , WEEK_IN_SECS = 604800; var socket , isInitialData = false @@ -1222,9 +1220,9 @@ var app = {}, browserSettings = {}, browserStorage = $.localStorage; if (offset > DAY_IN_SECS * 7) { parts.status = 'warn'; - } else if (offset < MINUTE_IN_SECS * -5 || offset > (MINUTE_IN_SECS * MINUTES_SINCE_LAST_UPDATE_URGENT)) { + } else if (offset < MINUTE_IN_SECS * -5 || offset > (MINUTE_IN_SECS * browserSettings.alarmTimeAgoUrgentMins)) { parts.status = 'urgent'; - } else if (offset > (MINUTE_IN_SECS * MINUTES_SINCE_LAST_UPDATE_WARN)) { + } else if (offset > (MINUTE_IN_SECS * browserSettings.alarmTimeAgoWarnMins)) { parts.status = 'warn'; } else { parts.status = 'current'; diff --git a/static/js/ui-utils.js b/static/js/ui-utils.js index b9ad5b547..7b1c4a54a 100644 --- a/static/js/ui-utils.js +++ b/static/js/ui-utils.js @@ -29,7 +29,9 @@ function getBrowserSettings(storage) { 'alarmLow': storage.get('alarmLow'), 'alarmUrgentLow': storage.get('alarmUrgentLow'), 'alarmTimeAgoWarn': storage.get('alarmTimeAgoWarn'), + 'alarmTimeAgoWarnMins': storage.get('alarmTimeAgoWarnMins'), 'alarmTimeAgoUrgent': storage.get('alarmTimeAgoUrgent'), + 'alarmTimeAgoUrgentMins': storage.get('alarmTimeAgoUrgentMins'), 'nightMode': storage.get('nightMode'), 'showRawbg': storage.get('showRawbg'), 'customTitle': storage.get('customTitle'), @@ -50,13 +52,17 @@ function getBrowserSettings(storage) { json.alarmLow = setDefault(json.alarmLow, app.defaults.alarmLow); json.alarmUrgentLow = setDefault(json.alarmUrgentLow, app.defaults.alarmUrgentLow); json.alarmTimeAgoWarn = setDefault(json.alarmTimeAgoWarn, app.defaults.alarmTimeAgoWarn); + json.alarmTimeAgoWarnMins = setDefault(json.alarmTimeAgoWarnMins, app.defaults.alarmTimeAgoWarnMins); json.alarmTimeAgoUrgent = setDefault(json.alarmTimeAgoUrgent, app.defaults.alarmTimeAgoUrgent); + json.alarmTimeAgoUrgentMins = setDefault(json.alarmTimeAgoUrgentMins, app.defaults.alarmTimeAgoUrgentMins); $('#alarm-urgenthigh-browser').prop('checked', json.alarmUrgentHigh).next().text('Urgent High Alarm' + appendThresholdValue(app.thresholds.bg_high)); $('#alarm-high-browser').prop('checked', json.alarmHigh).next().text('High Alarm' + appendThresholdValue(app.thresholds.bg_target_top)); $('#alarm-low-browser').prop('checked', json.alarmLow).next().text('Low Alarm' + appendThresholdValue(app.thresholds.bg_target_bottom)); $('#alarm-urgentlow-browser').prop('checked', json.alarmUrgentLow).next().text('Urgent Low Alarm' + appendThresholdValue(app.thresholds.bg_low)); $('#alarm-timeagowarn-browser').prop('checked', json.alarmTimeAgoWarn); + $('#alarm-timeagowarnmins-browser').val(json.alarmTimeAgoWarnMins); $('#alarm-timeagourgent-browser').prop('checked', json.alarmTimeAgoUrgent); + $('#alarm-timeagourgentmins-browser').val(json.alarmTimeAgoUrgentMins); json.nightMode = setDefault(json.nightMode, app.defaults.nightMode); $('#nightmode-browser').prop('checked', json.nightMode); @@ -358,7 +364,9 @@ $('#save').click(function(event) { 'alarmLow': $('#alarm-low-browser').prop('checked'), 'alarmUrgentLow': $('#alarm-urgentlow-browser').prop('checked'), 'alarmTimeAgoWarn': $('#alarm-timeagowarn-browser').prop('checked'), + 'alarmTimeAgoWarnMins': parseInt($('#alarm-timeagowarnmins-browser').val()) || 15, 'alarmTimeAgoUrgent': $('#alarm-timeagourgent-browser').prop('checked'), + 'alarmTimeAgoUrgentMins': parseInt($('#alarm-timeagourgentmins-browser').val()) || 30, 'nightMode': $('#nightmode-browser').prop('checked'), 'showRawbg': $('input:radio[name=show-rawbg]:checked').val(), 'customTitle': $('input#customTitle').prop('value'), @@ -373,7 +381,7 @@ $('#save').click(function(event) { $('#useDefaults').click(function(event) { //remove all known settings, since there might be something else is in localstorage - var settings = ['units', 'alarmUrgentHigh', 'alarmHigh', 'alarmLow', 'alarmUrgentLow', 'alarmTimeAgoWarn', 'alarmTimeAgoUrgent', 'nightMode', 'showRawbg', 'customTitle', 'theme', 'timeFormat']; + var settings = ['units', 'alarmUrgentHigh', 'alarmHigh', 'alarmLow', 'alarmUrgentLow', 'alarmTimeAgoWarn', 'alarmTimeAgoWarnMins', 'alarmTimeAgoUrgent', 'alarmTimeAgoUrgentMins', 'nightMode', 'showRawbg', 'customTitle', 'theme', 'timeFormat']; settings.forEach(function(setting) { browserStorage.remove(setting); }); From c1db1964d9469b46128fbeed9a4903b21ea34d2c Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Thu, 16 Apr 2015 23:36:51 -0700 Subject: [PATCH 3/4] make drawer submit button full width for better thumb access --- static/css/drawer.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static/css/drawer.css b/static/css/drawer.css index 6368e858e..559c69048 100644 --- a/static/css/drawer.css +++ b/static/css/drawer.css @@ -6,6 +6,7 @@ border-radius: 5px; border: 2px solid #aaa; box-shadow: 2px 2px 0 #eee; + width: 100%; } #drawer { @@ -42,7 +43,8 @@ input[type=number]:invalid { } #drawer .actions a { - padding-left: 20px; + float: right; + padding: 10px 0; } #treatmentDrawer { From 2ee1cab1f77effc01ee142e9ca03dce140a3c182 Mon Sep 17 00:00:00 2001 From: Jason Calabrese Date: Thu, 16 Apr 2015 23:38:37 -0700 Subject: [PATCH 4/4] don't change backgroud of batter pill when there's a stale data alarm; fixed position of snooze menu on smaller phones --- static/css/main.css | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/static/css/main.css b/static/css/main.css index 0d2e652d4..de90b71f5 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -109,17 +109,17 @@ body { color: red !important; } -.alarming-timeago .timeOther .pill.warn { +.alarming-timeago #lastEntry.pill.warn { border-color: yellow; } -.alarming-timeago .timeOther .pill.warn label { +.alarming-timeago #lastEntry.pill.warn label { background: yellow; } -.alarming-timeago .timeOther .pill.urgent { +.alarming-timeago #lastEntry.pill.urgent { border-color: red; } -.alarming-timeago .timeOther .pill.urgent label { +.alarming-timeago #lastEntry.pill.urgent label { background: red; } @@ -470,10 +470,8 @@ div.tooltip { font-size: 15px; } - .dropdown-menu { - font-size: 50px !important; - margin-left: 2vw; - width: 96vw; + #silenceBtn * { + font-size: 20px; } .time { @@ -619,7 +617,11 @@ div.tooltip { font-size: 15px; } - .focus-range { + #silenceBtn { + right: -25px; + } + + .focus-range { position: absolute; top: 40px; left: initial;