Skip to content

Commit

Permalink
Merge pull request #55 from nightscout/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
swissalpine authored Jun 20, 2023
2 parents 0858daf + f3afaec commit b7bfdae
Show file tree
Hide file tree
Showing 14 changed files with 590 additions and 85 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:14.15.3-alpine
FROM node:16.16.0-alpine

LABEL maintainer="Nightscout Contributors"

Expand Down
100 changes: 97 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ See [CONTRIBUTING.md](CONTRIBUTING.md)
- [`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)
- [`connect` (Nightscout Connect)](#connect-nightscout-connect)
- [`bridge` (Share2Nightscout bridge)](#bridge-share2nightscout-bridge), _deprecated_
- [`mmconnect` (MiniMed Connect bridge)](#mmconnect-minimed-connect-bridge), _deprecated_
- [`pump` (Pump Monitoring)](#pump-pump-monitoring)
- [`openaps` (OpenAPS)](#openaps-openaps)
- [`loop` (Loop)](#loop-loop)
Expand Down Expand Up @@ -488,8 +489,98 @@ autonomy for your data:
* `BOLUS_RENDER_FORMAT` (`default`) - Possible values are `hidden`, `default` (with leading zero and U), `concise` (with U, without leading zero), and `minimal` (without leading zero and U).
* `BOLUS_RENDER_FORMAT_SMALL` (`default`) - Possible values are `hidden`, `default` (with leading zero and U), `concise` (with U, without leading zero), and `minimal` (without leading zero and U).

##### `connect` (Nightscout Connect)

Connect common diabetes cloud resources to Nightscout.
Include the keyword `connect` in the `ENABLE` list.
Nightscout connection uses extended settings using the environment variable prefix `CONNECT_`.
* `CONNECT_SOURCE` - The name for the source of one of the supported inputs. one of `nightscout`, `dexcomshare`, etc...
###### Nightscout

> Work in progress
To sync from another Nightscout site, include `CONNECT_SOURCE_ENDPOINT` and
`CONNECT_SOURCE_API_SECRET`.
* `CONNECT_SOURCE=nightscout`
* `CONNECT_SOURCE_ENDPOINT=<URL>`
* `CONNECT_SOURCE_API_SECRET=<OPTIONAL_API_SECRET>`

The `CONNECT_SOURCE_ENDPOINT` must be a fully qualified URL and may contain a
`?token=<subject>` query string to specify an accessToken.
The `CONNECT_SOURCE_API_SECRET`, if provided, will be used to create a token
called `nightscout-connect-reader`. This information or the token provided in
the query will be used to read information from Nightscout and is optional if
the site is readable by default.

Select this driver by setting `CONNECT_SOURCE` equal to `nightscout`.



###### Dexcom Share
To synchronize from Dexcom Share use the following variables.
* `CONNECT_SOURCE=dexcomshare`
* `CONNECT_SHARE_ACCOUNT_NAME=`
* `CONNECT_SHARE_PASSWORD=`

Optional, `CONNECT_SHARE_REGION` and `CONNECT_SHARE_SERVER` do the same thing, only specify one.
* `CONNECT_SHARE_REGION=` `ous` or `us`. `us` is the default if nothing is
provided. Selecting `us` sets `CONNECT_SHARE_SERVER` to `share2.dexcom.com`.
Selecting `ous` here sets `CONNECT_SHARE_SERVER` to `shareous2.dexcom.com`.
* `CONNECT_SHARE_SERVER=` set the server domain to use.


###### Glooko

> Note: Experimental.
To synchronize from Glooko use the following variables.
* `CONNECT_SOURCE=glooko`
* `CONNECT_GLOOKO_EMAIL=`
* `CONNECT_GLOOKO_PASSWORD=`

By default, `CONNECT_GLOOKO_SERVER` is set to `api.glooko.com` because the
default value for `CONNECT_GLOOKO_ENV` is `default`.
* `CONNECT_GLOOKO_ENV` is the word `default` by defalt. Other values are
`development`, `production`, for `api.glooko.work`, and
`externalapi.glooko.com`, respectively.
* `CONNECT_GLOOKO_SERVER` the hostname server to use - `api.glooko.com` by `default`.

If both, `CONNECT_GLOOKO_SERVER` and `CONNECT_GLOOKO_ENV` are set, only
`CONNECT_GLOOKO_SERVER` will be used.

###### Libre Link Up
To synchronize from Libre Link Up use the following variables.
* `CONNECT_SOURCE=linkup`
* `CONNECT_LINK_UP_USERNAME=`
* `CONNECT_LINK_UP_PASSWORD=`

By default, `CONNECT_LINK_UP_SERVER` is set to `api-eu.libreview.io` because the
default value for `CONNECT_LINK_UP_REGION` is `EU`.
Other available values for `CONNECT_LINK_UP_REGION`:
* `US`, `EU`, `DE`, `FR`, `JP`, `AP`, `AU`, `AE`

For folks connected to many patients, you can provide the patient ID by setting
the `CONNECT_LINK_UP_PATIENT_ID` variable.

###### Minimed Carelink

To synchronize from Medtronic Minimed Carelink, set the following
environment variables.
* `CONNECT_SOURCE=minimedcarelink`
* `CONNECT_CARELINK_USERNAME`
* `CONNECT_CARELINK_PASSWORD`
* `CONNECT_CARELINK_REGION` Either `eu` to set `CONNECT_CARELINK_SERVER` to
`carelink.minimed.eu` or `us` to use `carelink.minimed.com`.

For folks using the new Many to Many feature, please provide the username of the
patient to follow using `CONNECT_CARELINK_PATIENT_USERNAME` variable.


##### `bridge` (Share2Nightscout bridge)
Glucose reading directly from the Dexcom Share service, uses these extended settings:

> **Deprecated** Please consider using the `connect` plugin instead.
Fetch glucose reading directly from the Dexcom Share service, uses these extended settings:
* `BRIDGE_USER_NAME` - Your username for the Share service.
* `BRIDGE_PASSWORD` - Your password for the Share service.
* `BRIDGE_INTERVAL` (`150000` *2.5 minutes*) - The time (in milliseconds) to wait between each update.
Expand All @@ -500,6 +591,9 @@ autonomy for your data:
* `BRIDGE_SERVER` (``) - The default blank value is used to fetch data from Dexcom servers in the US. Set to (`EU`) to fetch from European servers instead.

##### `mmconnect` (MiniMed Connect bridge)

> **Deprecated** Please consider using the `connect` plugin instead.
Transfer real-time MiniMed Connect data from the Medtronic CareLink server into Nightscout ([read more](https://github.com/mddub/minimed-connect-to-nightscout))
* `MMCONNECT_USER_NAME` - Your user name for CareLink Connect.
* `MMCONNECT_PASSWORD` - Your password for CareLink Connect.
Expand Down
9 changes: 1 addition & 8 deletions lib/api/alexa/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ function configure (app, wares, ctx, env) {

var handler = ctx.alexa.getIntentHandler(intentName, metric);
if (handler){
var sbx = initializeSandbox();
var sbx = ctx.sbx;
handler(next, slots, sbx);
return;
} else {
Expand All @@ -103,13 +103,6 @@ function configure (app, wares, ctx, env) {
}
}

function initializeSandbox() {
var sbx = require('../../sandbox')();
sbx.serverInit(env, ctx);
ctx.plugins.setProperties(sbx);
return sbx;
}

return api;
}

Expand Down
9 changes: 1 addition & 8 deletions lib/api/googlehome/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function configure (app, wares, ctx, env) {

var handler = ctx.googleHome.getIntentHandler(req.body.queryResult.intent.displayName, req.body.queryResult.parameters.metric);
if (handler){
var sbx = initializeSandbox();
var sbx = ctx.sbx;
handler(function (title, response) {
res.json(ctx.googleHome.buildSpeechletResponse(response, false));
next( );
Expand All @@ -46,13 +46,6 @@ function configure (app, wares, ctx, env) {

ctx.virtAsstBase.setupMutualIntents(ctx.googleHome);

function initializeSandbox() {
var sbx = require('../../sandbox')();
sbx.serverInit(env, ctx);
ctx.plugins.setProperties(sbx);
return sbx;
}

return api;
}

Expand Down
22 changes: 22 additions & 0 deletions lib/api2/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

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

const ddata = require('../data/endpoints')(env, ctx);
const notificationsV2 = require('./notifications-v2')(app, ctx);
const summary = require('./summary')(env, ctx);

app.use('/', apiv1);
app.use('/properties', ctx.properties);
app.use('/authorization', ctx.authorization.endpoints);
app.use('/ddata', ddata);
app.use('/notifications', notificationsV2);
app.use('/summary', summary);

return app;
}

module.exports = create;
File renamed without changes.
8 changes: 3 additions & 5 deletions lib/api/properties.js → lib/api2/properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ function create (env, ctx) {
ctx.authorization.isPermitted('api:treatments:read'));
properties.get(['/', '/*'], function getProperties (req, res) {

var sbx = sandbox.serverInit(env, ctx);

ctx.plugins.setProperties(sbx);
if (!ctx.sbx) res.json({});

function notEmpty (part) {
return ! _isEmpty(part);
Expand All @@ -38,10 +36,10 @@ function create (env, ctx) {
selected = _filter(segments[0].split(','), notEmpty);
}

var result = sbx.properties;
var result = ctx.sbx.properties;

if (selected.length > 0) {
result = _pick(sbx.properties, selected);
result = _pick(ctx.sbx.properties, selected);
}

result = env.settings.filteredSettings(result);
Expand Down
139 changes: 139 additions & 0 deletions lib/api2/summary/basaldataprocessor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
const { data } = require("jquery");

const dataProcessor = {};

function _hhmmAfter (hhmm, mills) {
var date = new Date(mills);
var withSameDate = new Date(
1900 + date.getYear()
, date.getMonth()
, date.getDate()
, parseInt(hhmm.substr(0, 2), 10)
, parseInt(hhmm.substr(3, 5), 10)
).getTime();
return withSameDate > date ? withSameDate : withSameDate + 24 * 60 * 60 * 1000;
}

// Outputs temp basal objects describing the profile temps for the duration
function _profileBasalsInWindow (basals, start, end) {
if (basals.length === 0) {
return [];
}

var i;
var out = [];

function nextProfileBasal () {
i = (i + 1) % basals.length;
var lastStart = out[out.length - 1].start;
return {
start: _hhmmAfter(basals[i]['time'], lastStart)
, absolute: parseFloat(basals[i]['value'])
, profile: 1
};
}

i = 0;
var startHHMM = new Date(start).toTimeString().substr(0, 5);
while (i < basals.length - 1 && basals[i + 1]['time'] <= startHHMM) {
i++;
}
out.push({
start: start
, absolute: parseFloat(basals[i]['value'])
, });

var next = nextProfileBasal();
while (next.start < end) {
out.push(next);
next = nextProfileBasal();
}

return out;
}

dataProcessor.filterSameAbsTemps = function filterSameAbsTemps (tempdata) {

var out = [];
var j = 0;

for (let i = 0; i < tempdata.length; i++) {
const temp = tempdata[i];

if (i == tempdata.length - 1) {
// If last was merged, skip
if (j != i) {
out.push(temp);
}
break;
}

const nextTemp = tempdata[i + 1];

if (temp.duration && (temp.start + temp.duration) >= nextTemp.start) {
if (temp.absolute == nextTemp.absolute) {
// Merge and skip next
temp.duration = nextTemp.start - temp.start + nextTemp.duration;
i += 1;
j = i;
} else {
// Adjust duration
temp.duration = nextTemp.start - temp.start;
}
}
out.push(temp);
}
return out;
}

dataProcessor.processTempBasals = function processTempBasals (profile, tempBasals, dataCap) {
var profileBasals = profile.basal;
var temps = tempBasals.map(function(temp) {
return {
start: new Date(temp['created_at']).getTime()
, duration: temp['duration'] === undefined ? 0 : parseInt(temp['duration'], 10) * 60 * 1000
, absolute: temp['absolute'] === undefined ? 0 : parseFloat(temp['absolute'])
};
}).concat([
{ start: Date.now() - 24 * 60 * 60 * 1000, duration: 0 }
, { start: Date.now(), duration: 0}
]).sort(function(a, b) {
return a.start - b.start;
});

var out = [];
temps.forEach(function(temp) {
var last = out[out.length - 1];
if (last && last.duration !== undefined && last.start + last.duration < temp.start) {
Array.prototype.push.apply(out, _profileBasalsInWindow(profileBasals, last.start + last.duration, temp.start));
}
if (temp.duration) out.push(temp);
});

var o2 = out;
var prevLength = 1;
var newLength = 0;

while (prevLength != newLength) {
prevLength = o2.length;
o2 = dataProcessor.filterSameAbsTemps(o2);
newLength = o2.length;
}

var o3 = [];

// Return temps from last hours
for (var i = 0; i < o2.length; i++) {
if ((o2[i].start + o2[i].duration) > dataCap) o3.push(o2[i]);
}

// Convert durations to seconds

for (var i = 0; i < o3.length; i++) {
o3[i].duration = o3[i].duration / 1000;
}

return o3;
}

module.exports = dataProcessor;
Loading

0 comments on commit b7bfdae

Please sign in to comment.