Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for nightscout-connect + easystate summary API v2 #7983

Merged
merged 18 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
135 changes: 135 additions & 0 deletions lib/api2/summary/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
'use strict';

function configure (env, ctx) {
const _ = require('lodash')
, basalProcessor = require('./basaldataprocessor')
, express = require('express')
, api = express.Router();

const defaultHours = 6;

api.use(ctx.wares.compression());

function removeProps(obj,keys){
if(Array.isArray(obj)){
obj.forEach(function(item){
removeProps(item,keys)
});
}
else if(typeof obj === 'object' && obj != null){
Object.getOwnPropertyNames(obj).forEach(function(key){
if(keys.indexOf(key) !== -1)delete obj[key];
else removeProps(obj[key],keys);
});
}
}

function processSGVs(sgvs, hours) {

const bgData = [];
const dataCap = Date.now() - (hours * 60 * 60 * 1000);

for (let i = 0; i < sgvs.length; i++) {
const bg = sgvs[i];
if (bg.mills >= dataCap) {

let item = {
sgv: bg.mgdl
, mills: bg.mills
};

// only push noise data if there is noise
if (bg.noise != 1) { item.noise = bg.noise; }
bgData.push(item);

}
}
return bgData;
}

// Collect treatments that contain insulin or carbs, temp basals
function processTreatments(treatments, profile, hours) {

const rVal = {
tempBasals: [],
treatments: [],
targets: []
};

let _temps = [];
const dataCap = Date.now() - (hours * 60 * 60 * 1000);

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

if (t.eventType == 'Temp Basal') {
_temps.push(t);
continue;
}
if (t.eventType == 'Temporary Target') {
rVal.targets.push({
targetTop: Math.round(t.targetTop),
targetBottom: Math.round(t.targetBottom),
duration: t.duration*60,
mills: t.mills
});
continue;
}

if (t.insulin || t.carbs) {
if (t.mills >= dataCap) {
const _t = {
mills: t.mills,
carbs: t.carbs,
insulin: t.insulin
}
rVal.treatments.push(_t);
}
continue;
}
}

rVal.tempBasals = basalProcessor.processTempBasals(profile,_temps, dataCap);

return rVal;
}

function constructState() {

const p = _.get(ctx, 'sbx.properties');

const state = {
iob: Math.round(_.get(p,'iob.iob')*100)/100,
cob: Math.round(_.get(p,'cob.cob')),
bwp: Math.round(_.get(p,'bwp.bolusEstimate')*100)/100,
cage: _.get(p,'cage.age'),
sage: _.get(p,'sage.age'),
iage: _.get(p,'iage.age'),
bage: _.get(p,'bage.age'),
battery: _.get(p,'upbat.level')
}
return state;
}

api.get('/', ctx.authorization.isPermitted('api:*:read'), function (req, res) {

const hours = req.query.hours || defaultHours;
const sgvs = processSGVs(ctx.ddata.sgvs, hours);
const profile = _.clone(ctx.sbx.data.profile.getCurrentProfile());
removeProps(profile,['timeAsSeconds']);
const treatments = processTreatments(ctx.ddata.treatments, profile, hours);
const state = constructState();

res.setHeader('content-type', 'application/json');
res.write(JSON.stringify({
sgvs,
treatments,
profile,
state
}));
res.end( );
});

return api;
}
module.exports = configure;
13 changes: 2 additions & 11 deletions lib/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,8 @@ function create (env, ctx) {
///////////////////////////////////////////////////
const apiRoot = require('../api/root')(env, ctx);
var api = require('../api/')(env, ctx);
var api2 = require('../api2/')(env,ctx, api);
var api3 = require('../api3/')(env, ctx);
var ddata = require('../data/endpoints')(env, ctx);
var notificationsV2 = require('../api/notifications-v2')(app, ctx);

app.use(compression({
filter: function shouldCompress (req, res) {
Expand Down Expand Up @@ -245,16 +244,8 @@ function create (env, ctx) {
app.use("/clock", clockviews);

app.use('/api', apiRoot);

app.use('/api/v1', api);

app.use('/api/v2', api);

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

app.use('/api/v2', api2);
app.use('/api/v3', api3);

// pebble data
Expand Down
Loading