diff --git a/README.md b/README.md index a0ee207f8f1..eb4f4ff3bb7 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) - [`bage` (Battery Age)](#bage-battery-age) - [`treatmentnotify` (Treatment Notifications)](#treatmentnotify-treatment-notifications) - [`basal` (Basal Profile)](#basal-basal-profile) + - [`bolus` (Bolus Rendering)](#bolus-bolus-rendering) - [`bridge` (Share2Nightscout bridge)](#bridge-share2nightscout-bridge) - [`mmconnect` (MiniMed Connect bridge)](#mmconnect-minimed-connect-bridge) - [`pump` (Pump Monitoring)](#pump-pump-monitoring) @@ -313,7 +314,6 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs/ or * The `linear` option has equidistant tick marks; the range used is dynamic so that space at the top of chart isn't wasted. * The `log-dynamic` is similar to the default `log` options, but uses the same dynamic range and the `linear` scale. * `EDIT_MODE` (`on`) - possible values `on` or `off`. Enables the icon allowing for editing of treatments in the main view. - * `BOLUS_RENDER_OVER` (1) - U value over which the bolus values are rendered on the chart if the 'x U and Over' option is selected. This value can be an integer or a float, e.g. 0.3, 1.5, 2, etc... ### Predefined values for your server settings (optional) * `INSECURE_USE_HTTP` (`false`) - Redirect unsafe http traffic to https. Possible values `false`, or `true`. Your site redirects to `https` by default. If you don't want that from Nightscout, but want to implement that with a Nginx or Apache proxy, set `INSECURE_USE_HTTP` to `true`. Note: This will allow (unsafe) http traffic to your Nightscout instance and is not recommended. @@ -470,12 +470,19 @@ To learn more about the Nightscout API, visit https://YOUR-SITE.com/api-docs/ or * `BAGE_URGENT` (`360`) - If time since last `Pump Battery Change` matches `BAGE_URGENT` hours, user will be issued a persistent warning of overdue change (default of 360 hours is 15 days). ##### `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). + Generates notifications when a treatment has been entered and snoozes alarms minutes after a treatment. + * `TREATMENTNOTIFY_SNOOZE_MINS` (`10`) - Number of minutes to snooze notifications after a treatment is entered + * `TREATMENTNOTIFY_INCLUDE_BOLUSES_OVER` (`0.25`) - U value over which the bolus will trigger a notification and snooze alarms ##### `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). Also uses the extended setting: * `BASAL_RENDER` (`none`) - Possible values are `none`, `default`, or `icicle` (inverted) +##### `bolus` (Bolus Rendering) + Settings to configure Bolus rendering + * `BOLUS_RENDER_OVER` (`0`) - U value over which the bolus labels rendered on the chart. This value can be an integer or a float, e.g. 0.3, 1.5, 2, etc... Use 0 to render All bolus labels, use `Number.MAX_SAFE_INTEGER` (9007199254740991) if you want None of the bolus labels to be rendered. + * `BOLUS_RENDER_FORMAT` (`default`) - Possible values are `default` (with leading zero and U), `concise` (with U, without leading zero), and `minimal` (without leading zero and U). + ##### `bridge` (Share2Nightscout bridge) Glucose reading directly from the Dexcom Share service, uses these extended settings: * `BRIDGE_USER_NAME` - Your username for the Share service. diff --git a/lib/client/browser-settings.js b/lib/client/browser-settings.js index 93313b41d9e..62e1c989ba4 100644 --- a/lib/client/browser-settings.js +++ b/lib/client/browser-settings.js @@ -13,6 +13,31 @@ function init (client, serverSettings, $) { var storage = Storages.localStorage; var settings = require('../settings')(); + function updateBolusRender () { + var bolusSettings = client.settings.extendedSettings.bolus || {}; + + var allRenderOverOptions = [1, 0.5, 0.1]; + if (_.isNumber(bolusSettings.renderOver) && bolusSettings.renderOver > 0 && bolusSettings.renderOver < Number.MAX_SAFE_INTEGER) { + allRenderOverOptions.push(_.toNumber(bolusSettings.renderOver)); + } + var sortedRenderOverOptions = _.chain(allRenderOverOptions).uniq().sort().reverse().value(); + + $('#bolusRenderOver').append(''); + + _.forEach(sortedRenderOverOptions, function (optionValue) { + $('#bolusRenderOver').append( + $('') + .attr('value', optionValue) + .text(client.translate('%1 U and Over', { params: [optionValue] })) + ); + }); + + $('#bolusRenderOver').append(''); + $('#bolusRenderOver').val(String(bolusSettings.renderOver || 0)); + $('#bolusRenderFormat').val(bolusSettings.renderFormat ? bolusSettings.renderFormat : 'default'); + + } + function loadForm () { var utils = client.utils; var language = client.language; @@ -70,7 +95,7 @@ function init (client, serverSettings, $) { $('#basalrender').val(settings.extendedSettings.basal ? settings.extendedSettings.basal.render : 'none'); - $('#bolusrender').val(settings.extendedSettings.bolus ? settings.extendedSettings.bolus.render : 'all'); + updateBolusRender(); if (settings.timeFormat === 24) { $('#24-browser').prop('checked', true); @@ -162,7 +187,7 @@ function init (client, serverSettings, $) { storage.remove(name); }); storage.remove('basalrender'); - storage.remove('bolusrender'); + storage.remove('bolus'); event.preventDefault(); client.browserUtils.reload(); }); @@ -215,7 +240,10 @@ function init (client, serverSettings, $) { , language: $('#language').val() , scaleY: $('#scaleY').val() , basalrender: $('#basalrender').val() - , bolusrender: $('#bolusrender').val() + , bolus: { + renderOver: $('#bolusRenderOver').val() + , renderFormat: $('#bolusRenderFormat').val() + } , showPlugins: checkedPluginNames() , storageVersion: STORAGE_VERSION }); @@ -276,11 +304,15 @@ function init (client, serverSettings, $) { settings.extendedSettings.basal.render = basalStored !== null ? basalStored : settings.extendedSettings.basal.render; if (!settings.extendedSettings.bolus) { - settings.extendedSettings.bolus = {}; + settings.extendedSettings.bolus = { + renderOver: 0 + , renderFormat: 'default' + }; } - var bolusStored = storage.get('bolusrender'); - settings.extendedSettings.bolus.render = bolusStored !== null ? bolusStored : settings.extendedSettings.bolus.render; + var bolusStored = storage.get('bolus'); + settings.extendedSettings.bolus.renderOver = bolusStored !== null ? _.toNumber(bolusStored.renderOver) : settings.extendedSettings.bolus.renderOver; + settings.extendedSettings.bolus.renderFormat = bolusStored !== null ? bolusStored.renderFormat : settings.extendedSettings.bolus.renderFormat; } catch (err) { console.error(err); diff --git a/lib/client/index.js b/lib/client/index.js index 16b08cf02a2..ab6d86d3b20 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -445,11 +445,6 @@ client.load = function load (serverSettings, callback) { } } - function updateBolusRenderOver () { - var bolusRenderOver = (client.settings.bolusRenderOver || 1) + ' U and Over'; - $('#bolusRenderOver').text(bolusRenderOver); - } - function alarmingNow () { return container.hasClass('alarming'); } @@ -1305,7 +1300,6 @@ client.load = function load (serverSettings, callback) { prepareEntries(); updateTitle(); - updateBolusRenderOver(); // Don't invoke D3 in headless mode diff --git a/lib/client/renderer.js b/lib/client/renderer.js index a3841695060..2c758d9c48a 100644 --- a/lib/client/renderer.js +++ b/lib/client/renderer.js @@ -527,7 +527,7 @@ function init (client, d3) { }; } - function prepareArc (treatment, radius, renderBasal) { + function prepareArc (treatment, radius, bolusSettings) { var arc_data = [ // white carb half-circle on top { 'element': '', 'color': 'white', 'start': -1.5708, 'end': 1.5708, 'inner': 0, 'outer': radius.R1 } @@ -565,11 +565,11 @@ function init (client, d3) { if (treatment.insulin > 0) { var dosage_units = '' + Math.round(treatment.insulin * 100) / 100; - if (renderBasal === 'all-remove-zero-u') { + if (_.includes(['concise', 'minimal'], bolusSettings.renderFormat)) { dosage_units = (dosage_units + "").replace(/^0/, ""); } - var unit_of_measurement = (renderBasal === 'all-remove-zero-u' ? '' : ' U'); // One international unit of insulin (1 IU) is shown as '1 U' + var unit_of_measurement = (bolusSettings.renderFormat === 'minimal' ? '' : ' U'); // One international unit of insulin (1 IU) is shown as '1 U' arc_data[3].element = dosage_units + unit_of_measurement; } @@ -990,7 +990,8 @@ function init (client, d3) { renderer.drawTreatments = function drawTreatments (client) { var treatmentCount = 0; - var renderBasal = client.settings.extendedSettings.bolus.render; + var bolusSettings = client.settings.extendedSettings.bolus || {}; + chart().focus.selectAll('.draggable-treatment').remove(); _.forEach(client.ddata.treatments, function eachTreatment (d) { @@ -999,17 +1000,18 @@ function init (client, d3) { // add treatment bubbles _.forEach(client.ddata.treatments, function eachTreatment (d) { - var showLabels = ( !d.carbs && ( ( renderBasal == 'none') || ( renderBasal === 'over' && d.insulin < client.settings.bolusRenderOver) ) ) ? false : true; + var showLabels = d.carbs || d.insulin > bolusSettings.renderOver; renderer.drawTreatment(d, { scale: renderer.bubbleScale() , showLabels: showLabels , treatments: treatmentCount - }, client.sbx.data.profile.getCarbRatio(new Date()), - renderBasal); + } + , client.sbx.data.profile.getCarbRatio(new Date()) + , bolusSettings); }); }; - renderer.drawTreatment = function drawTreatment (treatment, opts, carbratio, renderBasal) { + renderer.drawTreatment = function drawTreatment (treatment, opts, carbratio, bolusSettings) { if (!treatment.carbs && !treatment.protein && !treatment.fat && !treatment.insulin) { return; } @@ -1027,7 +1029,7 @@ function init (client, d3) { return; } - var arc = prepareArc(treatment, radius, renderBasal); + var arc = prepareArc(treatment, radius, bolusSettings); var treatmentDots = appendTreatments(treatment, arc); appendLabels(treatmentDots, arc, opts); }; diff --git a/lib/plugins/bolus.js b/lib/plugins/bolus.js new file mode 100644 index 00000000000..bc2f9a322b9 --- /dev/null +++ b/lib/plugins/bolus.js @@ -0,0 +1,22 @@ +'use strict'; + +function init () { + + var bolus = { + name: 'bolus' + , label: 'Bolus' + , pluginType: 'fake' + }; + + bolus.getPrefs = function getPrefs(sbx) { + return { + renderFormat: sbx.extendedSettings.renderFormat ? sbx.extendedSettings.renderFormat : 'default' + , renderOver: sbx.extendedSettings.renderOver ? sbx.extendedSettings.renderOver : 0 + , notifyOver: sbx.extendedSettings.notifyOver ? sbx.extendedSettings.notifyOver : 0 + }; + }; + + return bolus; +} + +module.exports = init; \ No newline at end of file diff --git a/lib/plugins/index.js b/lib/plugins/index.js index d6bae73bdbc..fefba1406b9 100644 --- a/lib/plugins/index.js +++ b/lib/plugins/index.js @@ -46,6 +46,7 @@ function init (ctx) { , require('./insulinage')(ctx) , require('./batteryage')(ctx) , require('./basalprofile')(ctx) + , require('./bolus')(ctx) // fake plugin to hold extended settings , require('./boluscalc')(ctx) // fake plugin to show/hide , require('./profile')(ctx) // fake plugin to hold extended settings , require('./speech')(ctx) @@ -128,7 +129,7 @@ function init (ctx) { }; //these plugins are either always on or have custom settings - plugins.specialPlugins = 'ar2 bgnow delta direction timeago upbat rawbg errorcodes profile'; + plugins.specialPlugins = 'ar2 bgnow delta direction timeago upbat rawbg errorcodes profile bolus'; plugins.shownPlugins = function(sbx) { return _filter(enabledPlugins, function filterPlugins (plugin) { diff --git a/lib/plugins/treatmentnotify.js b/lib/plugins/treatmentnotify.js index cc979aa09b5..bf3b698250c 100644 --- a/lib/plugins/treatmentnotify.js +++ b/lib/plugins/treatmentnotify.js @@ -20,12 +20,17 @@ function init(ctx) { function filterTreatments (sbx) { var treatments = sbx.data.treatments; + var includeBolusesOver = sbx.extendedSettings.includeBolusesOver || 0.25; + treatments = _.filter(treatments, function notOpenAPS (treatment) { var ok = true; var enteredBy = treatment.enteredBy; if (enteredBy && (enteredBy.indexOf('openaps://') === 0 || enteredBy.indexOf('loop://') === 0)) { ok = _.indexOf(MANUAL_TREATMENTS, treatment.eventType) >= 0; } + if (ok && _.isNumber(treatment.insulin) && _.includes(['Meal Bolus', 'Correction Bolus'], treatment.eventType)) { + ok = treatment.insulin >= includeBolusesOver; + } return ok; }); diff --git a/lib/settings.js b/lib/settings.js index 0982c63a821..fc3b71d9757 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -52,7 +52,6 @@ function init () { , deNormalizeDates: false , showClockDelta: false , showClockLastTime: false - , bolusRenderOver: 1 , frameUrl1: '' , frameUrl2: '' , frameUrl3: '' @@ -170,7 +169,7 @@ function init () { } //TODO: getting sent in status.json, shouldn't be - settings.DEFAULT_FEATURES = ['bgnow', 'delta', 'direction', 'timeago', 'devicestatus', 'upbat', 'errorcodes', 'profile', 'dbsize', 'runtimestate', 'basal', 'careportal']; + settings.DEFAULT_FEATURES = ['bgnow', 'delta', 'direction', 'timeago', 'devicestatus', 'upbat', 'errorcodes', 'profile', 'bolus', 'dbsize', 'runtimestate', 'basal', 'careportal']; var wasSet = []; diff --git a/views/index.html b/views/index.html index b9a0a564585..720721c5ec9 100644 --- a/views/index.html +++ b/views/index.html @@ -217,13 +217,18 @@